接上一章节Feign实现服务消费的熔断及熔断的异常信息获取,这里讲讲使用Zuul构建Api网关
API 网关 Zuul
Spring Cloud 的 Zuul 是什么
通过前面的内容,我们已经可以基本搭建出一套简略版的微服务架构了,我们有注册中心 Eureka,可以将服务注册到该注册中心中,我们有 Ribbon 或Feign 可以实现对服务负载均衡地调用,我们有 Hystrix 可以实现服务的熔断,但是我们还缺少什么呢?
我们首先来看一个微服务架构图:
在上面的架构图中,我们的服务包括:内部服务 Service A 和内部服务 Service B,这两个服务都是集群部署,每个服务部署了 3 个实例,他们都会通过 Eureka Server 注册中心注册与订阅服务,而 Open Service 是一个对外的服务,也是集群部署,外部调用方通过负载均衡设备调用 Open Service 服务,比如负载均衡使用 Nginx,这样的实现是否合理,或者是否有更好的实现方式呢?
1、如果我们的微服务中有很多个独立服务都要对外提供服务,那么我们要如何去管理这些接口?特别是当项目非常庞大的情况下要如何管理?
2、在微服务中,一个独立的系统被拆分成了很多个独立的服务,为了确保安全,权限管理也是一个不可回避的问题,如果在每一个服务上都添加上相同的权限验证代码来确保系统不被非法访问,那么工作量也就太大了,而且维护也非常不方便。
为了解决上述问题,微服务架构中提出了 API 网关的概念,它就像一个安检站一样,所有外部的请求都需要经过它的调度与过滤,然后 API 网关来实现请求路由、负载均衡、权限验证等功能;
那么 Spring Cloud 这个一站式的微服务开发框架基于 Netflix Zuul 实现了Spring Cloud Zuul,采用 Spring Cloud Zuul 即可实现一套 API 网关服务。
使用Zuul构建Api网关
1、创建一个普通的 Spring Boot 工程,导入相关依赖
<!--SpringCloud 集成 eureka 客户端的起步依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--添加 spring cloud 的 zuul 的起步依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<!--SpringCloudx相关依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--指定SpringCloud仓库-->
<repositories>
<repository><id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
2、在入口类上添加@EnableZuulProxy 注解,开启 Zuul 的 API 网关服务功能
@EnableZuulProxy //开启 Zuul 的 API 网关服务功能
3、在 application.properties 文件中配置路由规则
#配置服务内嵌的 Tomcat 端口
server.port=8084
#配置服务的名称
spring.application.name=springcloud-api-gateway
#配置 API 网关到注册中心上,API 网关也将作为一个服务注册到 eureka-server 上
eureka.client.service-url.defaultZone=http://eureka8761:8761/eureka/,http://eureka8762:8762/eureka/
#配置路由规则 zuul.routes.接自定义名字
zuul.routes.api-lixuanhong.path=/api-lixuanhong/**
zuul.routes.api-lixuanhong.serviceId=springcloud-service-feign
以上配置,我们的路由规则就是匹配所有符合/api-lixuanhong/**的请求,只要路径中带有/api-wkcto/都将被转发到 springcloud-service-feign 服务上,至于springcloud-service-feign 服务的地址到底是什么则由 eureka-server 注册中心去分析,我们只需要写上服务名即可。
以我们目前搭建的项目为例,请求 http://localhost:8084/api-lixuanhong/web/hello 接口则相当于请求 http://localhost:8083/web/hello
(springcloud-service-feign 服务的地址为 http://localhost:8083/web/hello), 路由规则中配置的 api-lixuanhong是路由的名字,可以任意定义,但是一组 path 和serviceId 映射关系的路由名要相同。
如果以上测试成功,则表示们的 API 网关服务已经构建成功了,我们发送的符合路由规则的请求将自动被转发到相应的服务上去处理。
4、修改springboot,springcloud以及相关依赖版本号
最新的springcloud,最新的不支持zuul;用2020以上版本springcloud。能够使用zuul包,并能注册到eureka,但是服务访问不了,500内部服务错误;最后改为2020.04版本以下的springcloud才行,这里我们选择的Spring Boot 2.2.x, 2.3.x (SR5+)对应SpringCloud的Hoxton版本
- 依赖如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lixuanhong.springcloud</groupId>
<artifactId>springcloud-api-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-api-gateway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--SpringCloud 集成 eureka 客户端的起步依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--添加 spring cloud 的 zuul 的起步依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
<!--SpringCloudx相关依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--指定SpringCloud仓库-->
<repositories>
<repository><id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5、为Zuul配置超时时间
- 由于服务提供者一的请求方法里是阻塞线程,那么我们Zuul请求转发到feign上负载均衡到服务提供者一上时会报Gateway Timeout,所以需要为Zuul配置超时时间
ribbon.ReadTimeout=5000
ribbon.ConnectTimeout=5000
ribbon.ReadTimeout, ribbon.SocketTimeout这两个就是ribbon超时时间设置。还有zuul.host.connect-timeout-millis, zuul.host.socket-timeout-millis这两个配置,这两个和上面的ribbon都是配超时的。区别在于,如果路由方式是serviceId的方式,那么ribbon的生效,如果是url的方式,则zuul.host开头的生效。(此处重要!使用serviceId路由和url路由是不一样的超时策略)如果你在zuul配置了熔断fallback的话,熔断超时也要配置,不然如果你配置的ribbon超时时间大于熔断的超时,那么会先走熔断,相当于你配的ribbon超时就不生效了
6、启动测试
- 我们服务提供者二是有异常的,所以返回