由于后起之秀 Gateway 的诞生,不建议在新项目中再使用 Zuul。
序言
当客户端发出一些对微服务获取资源的请求到后端,这些请求将通过 FS、 Nginx 等设施的路由和负载均衡分配并转发到各个不同的服务实例上。为了让这些设施能够正确路由与分发请求,运维人员需要手工维护这些路由规则与服务实例列表, 当有实例增减或是 IP 地址变动等情况发生的时候,也需要手工地去同步修改这些信息以保持实例信息与中间件的一致。当系统规模不断增大时,这些看似简单的维护任务会变得越来越麻烦,且配置出错概率也会增加。
很显然,上述做法并不可取,因此,我们需要一套机制来有效降低维护路由规则与服务实例列表的难度。
对于某些服务的权限校验(如用户登录状态的校验),若突然发现校验逻辑有个 BUG 需要修复,或者需要对其做一些扩展和优化,此时就不得不去每个应用里修改这些逻辑,这样的修改不仅会引起开发入员的抱怨,更会加重测试人员的负担。所以,我们也需要一套机制,能够很好地解决微服务架构中,对于微服务接口访问时各前置校验的冗余问题。
这时候就可以使用 API 网关,Zuul 就是一种网关。
Zuul:API 网关
Spring Cloud 中了提供了基于 Netflix Zuul 实现的 API 网关组件: Zuul。
对于路由规则与服务实例的维护问题,Zuul 通过与 Eureka 进行整合,将自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获得了所有其他微服务的实例信息。这样将维护服务实例的工作交给了服务治理框架自动完成,不再需要人工介入。
而对于路由规则的维护, Zuul 默认会将通过以服务名作为 ContextPath 的方式来创建路由映射。大部分情况下,这样的默认设置已经可以实现开发人员大部分的路由需求,大大减少了运维的工作量。
快速入门
① 添加依赖
首先,我们新建一个名为zuul-gateway
的模块,并在pom.xml
文件加入下面的依赖:1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
② 添加注解
新建启动类并添加@EnableZuulProxy
注解:1
2
3
4
5
6
7
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class);
}
}
③ 创建配置文件
在resources
下创建application.yml
文件,并添加:1
2
3
4
5
6
7server:
port: 10010
zuul:
routes:
servername:
path: /user-service/**
url: http://127.0.0.1:8081
其中 :
servername
代表服务名,可以随意命名path
代表请求路径url
代表请求路径转发的服务器
简单来说,启动 Zuul 服务器后, 我们访问http://127.0.0.1:10010/user-service/
下的请求都会转发到http://127.0.0.1:8081
的服务器,也即我们前面配置的user-service
服务器。
④ 测试
访问http://127.0.0.1:10010/user-service/user/1
进行测试,输入结果如下:1
{"id":1,"username":"lucy","password":"123","name":"章总","telephone":0}
问题与优化
我们发现,在application.yml
中对转发的服务进行硬编码,不利于维护,而且,若有集群存在时无法实现负载均衡。
路由配置
对 Zuul 而言,不应该直接去访问具体的服务器,而是应该先去 Eureka 注册中心拉去相关的服务,然后再进行访问。我们称其为面向服务的路由。
因此我们需要将 Zuul 也注册到 Eureka 中,首先在zuul-gateway
模块添加 Euraka 的依赖;1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
然后修改application.yml
:1
2
3
4
5
6
7
8
9
10
11
12
13
14server:
port: 10010
spring:
application:
name: zuul-gateway
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
zuul:
routes:
servername:
path: /user-service/**
serviceId: user-service
这样让 Zuul 去访问 Eureka 的提供的服务 Id 即可。
由于servername
没啥用处,所有我们可以直接配置serviceId
和路径path
:1
2
3zuul:
routes:
user-service: /user-service/**
其中user-service
是服务 Id,而/user-service/**
是映射的路径。
默认的路由配置
大多数情况下,路由的名称和服务名相同。
由于这样的配置太过于常见,所以上面的配置可以全部不写,全部不写,全部不写!!!
Zuul 默认转换 Eureka 注册中心所有的服务,并且还实现了反向代理和负载均衡。
路由前缀
我们可以通过以下配置来指定路由前缀:1
2zuul:
prefix: /api
这样在发起请求时,路由就要以/api
开头
自定义配置
当然,若觉得 Zuul 默认配置的路径太长或者某些服务不想对外界暴露(只提供服务间调用),则可以进行自定义配置:
1 | zuul: |
这么配置将 Eureka 提供的user-service
服务映射路径缩短为/user/**
。
ignored-services
参数代表对外界关闭的服务列表(一个 Set 集合,用-
分隔),这里我们将consumer
服务对外界关闭了。
过滤器
Zuul 作为网关的一个重要功能就是实现请求的鉴权,这个动作往往是通过 Zuul 提供的过滤器来实现。
负载均衡和熔断
Zuul 中已默认集成了 Ribbon 负载均衡和 Hystix 熔断机制。
但是,所有的超时策略都走的默认值,如熔断超时时间只有 1 S,很容易就触发了。
因此,我们最好手动配置:1
2
3
4
5
6
7
8
9
10
11
12
13hystrix:
commond:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000
ribbon:
CoonectionTimeout: 500 # 连接超时时长
ReadTimeout: 2000 # 读取超时时长
MaxAutoRetries: 0 # 当前服务重试次数
MaxAutoRetriesNextServer: 0 # 切换服务重试次数
注意哦
:Ribbon 的超时时长为(连接超时时长 + 读取超时时长) X 2,且必须小于 Hystrix 时长。
参考
- Spring Cloud 官方文档
- 翟永超. Spring Cloud 微服务实战 [M]. 电子工业出版社,2017