芋道 Spring Cloud Netflix 网关 Zuul 入门
总阅读量:次
摘要: 原创出处 http://www.iocoder.cn/Spring-Cloud/Netflix-Zuul/ 「芋道源码」欢迎转载,保留摘要,谢谢!
- 1. 概述
- 2. 为什么使用网关?
- 3. 快速入门
- 4. 基于注册中心实现动态路由
- 5. 基于配置中心 Apollo 实现动态路由
- 6. 基于配置中心 Nacos 实现动态路由
- 7. 灰度发布
- 8. Zuul 过滤器
- 9. 请求限流
- 10. 基于 Hystrix 实现服务容错
- 11. 基于 Sentinel 实现服务容错
- 12. 监控端点
- 666. 彩蛋
🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:
- RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
- RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
- 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
- 新的源码解析文章实时收到通知。每周更新一篇左右。
- 认真的源码交流微信群。
1. 概述
Zuul 是由 Netflix 开源的微服务网关,提供都动态路由、监控、熔断、安全等等功能。
Zuul is a gateway service that provides dynamic routing, monitoring, resiliency, security, and more.
Spring Cloud Netflix Zuul 将 Zuul 融入 Spring Cloud 生态体系,作为 Spring Cloud 微服务架构中的 API 网关。如下图所示:
拓展小知识:
Zuul 有 1.X 和 2.X 两个版本,前者基于同步阻塞模式的编程模型实现,后者基于异步非阻塞模式的编程模型实现。两者的对比,可以看看[《Zuul1 和 Zuul2 该如何选择?》](https://blog.csdn.net/yang75108/article/details/86991401 TODO)文章。
目前,Spring Cloud Netflix Zuul 采用的是 Zuul 1.X 版本。并且,Zuul 2.X 不会被集成到 Spring Cloud 中,具体可见 ISSUE#86。
可能因为 Spring Cloud 团队已经开源了 Spring Cloud Gateway 网关,而它和 Zuul 2.X 一样,也是基于异步非阻塞模式的编程模型实现。
所以胖友如果是新学 Spring Cloud 网关相关的内容,建议优先选择《芋道 Spring Cloud 网关 Spring Cloud Gateway 入门》。
2. 为什么使用网关?
胖友可以后续阅读如下文章:
🙂 先继续 Zuul 的入门。
3. 快速入门
示例代码对应仓库:
labx-21-sc-zuul-demo01
本小节我们来对 Zuul 进行快速入门。创建一个 labx-21-sc-zuul-demo01
项目,最终项目结构如下图:
3.1 引入依赖
创建 pom.xml
文件中,主要引入 Spring Cloud Gateway 相关依赖。代码如下:
<?xml version="1.0" encoding="UTF-8"?> |
在 spring-cloud-starter-netflix-zuul
依赖中,会引入 Zuul 1.X 等等依赖,如下图所示:
3.2 配置文件
创建 application.yaml
配置文件,添加 Spring Cloud Zuul 相关配置。配置如下:
server: |
① server.port
配置项,设置网关的服务器端口。
② zuul
配置项,Spring Cloud Zuul 配置项,对应 ZuulProperties 类。
servlet-path
配置项,设置 ZuulServlet 匹配的路径,默认为 /zuul
。Zuul 和 SpringMVC 一样,都是通过实现自定义的 Servlet,从而进行请求的转发。如下图所示:
routes
配置项,对应 ZuulRoute Map。其中 key 为路由编号,value 为路由具体配置:
path
:匹配的 URL 地址。url
:转发的 URL 地址。
这里,我们将以 /blog/**
开头的 URL 转发到 http://www.iocoder.cn,以 /oschina/**
开头的 URL 转发到 https://www.oschina.net。
3.3 ZuulApplication
创建 ZuulApplication 类,网关的启动类。代码如下:
|
在类上,添加 @EnableZuulProxy
注解,声明开启 Zuul 网关功能。
3.4 简单测试
① 使用 ZuulApplication 启动网关。可以看到控制台打印 Spring Cloud Zuul 相关日志如下:
2020-05-03 18:16:26.568 INFO 44488 --- [ main] o.s.c.n.zuul.ZuulFilterInitializer : Starting filter initializer |
② 使用浏览器,访问 http://127.0.0.1:8888/blog,成功转发到目标 URI http://www.iocoder.cn
,如下图所示:
③ 使用浏览器,访问 http://127.0.0.1:8888/oschina,成功转发到目标 URI http://www.oschina.net
,如下图所示:
4. 基于注册中心实现动态路由
示例代码对应仓库:
在「3. 快速入门」小节,我们在配置文件中,通过 path
+ url
的组合,添加了两个路由信息,这种我们一般称之为“静态路由”或者“传统路由”。
本小节,我们将通过 path
+ service-id
的组合,添加路由信息,这种我们一般称之为“动态路由”。如此,每个路由转发的 URL 地址,将从 Spring Cloud 注册中心获取对应 service-id
服务名对应的服务实例列表,并通过 Ribbon 等等进行负载均衡。
我们直接从「3. 快速入门」小节的 labx-21-sc-zuul-demo01
项目来复制,搭建 Zuul 基于注册中心实现动态路由的示例。最终项目结构如下图:
分割线:先进行网关项目的改造。
4.1 引入依赖
修改 pom.xml
文件,引入注册中心 Nacos 相关的依赖如下:
<!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖,将 Nacos 作为注册中心,并实现对其的自动配置 --> |
考虑到 Nacos 作为 Spring Cloud 架构中的注册中心,已经越来越流行了,所以本小节我们使用它。感兴趣的胖友,可以后面看看艿艿写的《芋道 Spring Cloud Alibaba 注册中心 Nacos 入门》文章。
友情提示:如果胖友想用使用 Eureka 作为注册中心,可以引入
spring-cloud-starter-netflix-eureka-client
依赖。
4.2 配置文件
修改 application.yaml
配置文件,增加注册中心相关的配置项。完整配置如下:
server: |
① spring.cloud.nacos.discovery
配置项,使用 Nacos 作为 Spring Cloud 注册中心的配置项。这里就不详细解释,毕竟 Nacos 不是主角。
② 在 zuul.routes
配置项中,添加“动态路由” route_users
,通过 service-id
配置路由对应 user-service
用户服务。
4.3 搭建用户服务
创建 labx-21-sc-user-service
项目,作为 user-service
用户服务。代码比较简单,艿艿就不瞎哔哔了。最终项目如下图所示:
4.4 简单测试
① 执行 UserServiceApplication 两次,启动两个 user-service
服务。启动完成后,在 Nacos 注册中心可以看到该服务的两个实例,如下图所示:
② 执行 ZuulApplication 启动网关。
③ 访问 http://127.0.0.1:8888/users/user/get?id=1 地址,返回 JSON 结果如下:
{ |
请求经过网关后,转发到 user-service
服务成功。
另外,Spring Cloud Zuul 集成 Spring Cloud 注册中心时,会给注册中心的每个服务在 Zuul 中自动创建对应的“动态路由”。例如说本小节的示例,对应配置文件的效果如下:
zuul: |
我们来简单测试下,访问 http://127.0.0.1:8888/user-service/user/get?id=1 地址,返回 JSON 结果如下:
{ |
友情提示:Zuul 通过 RibbonRoutingFilter 过滤器,使用 Ribbon 负载均衡请求后端的服务实例。
更多关于 Ribbon 的内容,胖友可以阅读《芋道 Spring Cloud Netflix 负载均衡 Ribbon 入门》文章。
5. 基于配置中心 Apollo 实现动态路由
示例代码对应仓库:
在「4. 基于注册中心实现动态路由」小节中,我们在配置文件中,添加了 Zuul “动态路由”。但是,如果每次进行路由的变更时,都需要修改配置文件,并重启 Zuul 实例,显然是不合适的。
因此,我们可以引入配置中心 Apollo 来实现动态路由的功能,将 zuul
配置项统一存储在 Apollo 中。同时,通过通过 Apollo 的实时监听器,在 zuul
发生变化时,刷新内存中的路由信息。
当然,Gateway 中我们还是会使用注册中心,目的是为了获取服务的实例列表,只是不再使用 Gateway 基于注册中心来的动态路由功能而已。
我们直接从「4. 基于注册中心实现动态路由」小节的 labx-21-sc-zuul-demo02-registry
项目,复制出本小节的 labx-21-sc-zuul-demo03-config-apollo
项目,搭建 Zuul 基于配置中心 Apollo 实现动态路由的示例。最终项目结构如下图:
分割线:先进行网关项目的改造。
5.1 引入依赖
修改 pom.xml
文件,引入配置中心 Apollo 相关的依赖如下:
<!-- 引入 Apollo 客户端,内置对 Apollo 的自动化配置 --> |
5.2 配置文件
修改 application.yaml
配置文件,增加 Apollo 相关的配置项。完整配置如下:
server: |
① zuul
配置项,我们都删除了,统一在 Apollo 中进行配置。
为了演示 Gateway 启动时,从 Apollo 加载 spring.cloud.gateway
配置项,作为初始的路由信息,我们在 Apollo 配置如下:
- 默认将请求转发到艿艿的博客 http://www.iocoder.cn,嘿嘿~
配置对应文本内容如下:
zuul.servlet-path = / |
② app.id
和 apollo
配置项,为 Apollo 相关配置项。这里就不详细解释,毕竟 Apollo 不是主角。感兴趣的胖友,可以阅读《芋道 Spring Boot 配置中心 Apollo 入门》文章。
5.3 ZuulPropertiesRefresher
创建 ZuulPropertiesRefresher 类,监听 Apollo 中的zuul
发生变化时,刷新内存中的路由信息。代码如下:
|
① <1>
处,通过 Apollo 提供的 @ApolloConfigChangeListener
注解,声明监听 zuul.
配置项的刷新。
② <2>
处,发布 EnvironmentChangeEvent 配置变更事件,从而被 Spring Cloud Context ConfigurationPropertiesRebinder 所监听,刷新 ZuulProperties 配置类。
③ <3>
处,发布 RoutesRefreshedEvent 路由刷新事件,从而被 Spring Cloud Zuul ZuulServerAutoConfiguration.ZuulRefreshListener 所监听,刷新内存中的 Zuul 路由信息。
友情提示:这块涉及一定的知识量,胖友可以通过在对应的 Listener 监听器,打上相应的断点进行调试。
5.4 搭建用户服务
创建 labx-21-sc-user-service
项目,作为 user-service
用户服务。代码比较简单,艿艿就不瞎哔哔了。最终项目如下图所示:
5.5 简单测试
① 执行 UserServiceApplication 两次,启动两个 user-service
服务。
② 执行 ZuulApplication 启动网关。
使用浏览器,访问 http://127.0.0.1:8888/ 地址,返回艿艿的博客首页,如下图所示:
③ 修改在 Apollo 的 zuul
配置项,转发请求到用户服务。如下图所示:
配置对应文本内容如下:
zuul.servlet-path = / |
此时 IDEA 控制台看到 GatewayPropertiesRefresher 监听到 zuul
配置项刷新,并打印日志如下:
2020-05-04 00:26:31.763 INFO 52628 --- [Apollo-Config-2] c.i.s.l.z.ZuulPropertiesRefresher : Refreshing zuul properties! |
④ 访问 http://127.0.0.1:8888/user/get?id=1 地址,返回 JSON 结果如下:
{ |
请求经过网关后,转发到 user-service
服务成功。
6. 基于配置中心 Nacos 实现动态路由
示例代码对应仓库:
在「4. 基于注册中心实现动态路由」小节中,我们在配置文件中,添加了 Zuul “动态路由”。但是,如果每次进行路由的变更时,都需要修改配置文件,并重启 Zuul 实例,显然是不合适的。
因此,我们可以引入配置中心 Config 来实现动态路由的功能,将 zuul
配置项统一存储在 Config 中。同时,通过通过 Config 的实时监听器,在 zuul
发生变化时,刷新内存中的路由信息。
当然,Zuul 中我们还是会使用注册中心,目的是为了获取服务的实例列表,只是不再使用 Zuul 基于注册中心来的动态路由功能而已。
我们直接从「4. 基于注册中心实现动态路由」小节的 labx-21-sc-zuul-demo02-registry
项目,复制出本小节的 labx-21-sc-zuul-demo03-config-nacos
项目,搭建 Zuul 基于配置中心 Nacos 实现动态路由的示例。最终项目结构如下图:
分割线:先进行网关项目的改造。
6.1 引入依赖
修改 pom.xml
文件,引入配置中心 Nacos 相关的依赖如下:
<!-- 引入 Spring Cloud Alibaba Nacos Config 相关依赖,将 Nacos 作为配置中心,并实现对其的自动配置 --> |
6.2 配置文件
① 创建 bootstrap.yaml
配置文件,添加配置中心 Nacos 相关的配置。配置如下:
spring: |
spring.cloud.nacos.config
配置项,为配置中心 Nacos 相关配置项。这里就不详细解释,毕竟 Nacos 不是主角。感兴趣的胖友,可以阅读《芋道 Spring Cloud Alibaba 配置中心 Nacos 入门》文章。
② 修改 application.yaml
配置文件,删除 Zuul 相关的配置。完整配置如下:
server: |
zuul
配置项,我们都删除了,统一在配置中心 Nacos 中进行配置。
为了演示 Zuul 启动时,从 Nacos 加载 zuul
配置项,作为初始的路由信息,我们在 Nacos 配置如下:
- 默认将请求转发到艿艿的博客 http://www.iocoder.cn,嘿嘿~
配置对应文本内容如下:
zuul: |
6.3 ZuulRouteRefreshListener
在 Nacos 配置发生变化时,Spring Cloud Alibaba Nacos Config 内置的监听器 会监听到配置刷新,发布 EnvironmentChangeEvent 配置变更事件。因此,我们实现自定义 ZuulRouteRefreshListener 监听器,来监听该事件。代码如下:
|
① <1>
处,判断 zuul.
配置项,是否发生变化。
② <2>
处,发布 RoutesRefreshedEvent 路由刷新事件,从而被 Spring Cloud Zuul ZuulServerAutoConfiguration.ZuulRefreshListener 所监听,刷新内存中的 Zuul 路由信息。
友情提示:这块涉及一定的知识量,胖友可以通过在对应的 Listener 监听器,打上相应的断点进行调试。
6.4 搭建用户服务
创建 labx-21-sc-user-service
项目,作为 user-service
用户服务。代码比较简单,艿艿就不瞎哔哔了。最终项目如下图所示:
6.5 简单测试
① 执行 UserServiceApplication 两次,启动两个 user-service
服务。
② 执行 ZuulApplication 启动网关。
使用浏览器,访问 http://127.0.0.1:8888/ 地址,返回艿艿的博客首页,如下图所示:
③ 修改在 Nacos 的 zuul
配置项,转发请求到用户服务。如下图所示:
配置对应文本内容如下:
zuul: |
此时 IDEA 控制台看到 ZuulRouteRefreshListener 监听到 zuul.
配置项刷新,并打印日志如下:
④ 访问 http://127.0.0.1:8888/user/get?id=1 地址,返回 JSON 结果如下:
{ |
请求经过网关后,转发到 user-service
服务成功。
7. 灰度发布
Spring Cloud Zuul 并未提供灰度发布功能。
友情提示:感兴趣的胖友,可以阅读《芋道 Spring Cloud 网关 Spring Cloud Gateway 入门》的「8. 灰度发布」小节。
8. Zuul 过滤器
Zuul 的两大核心是路由和过滤功能:
- 路由功能:负责匹配请求到指定 Route 路由,从而转发请求到该路由对应的
url
后端 URL 或service-id
服务实例上。 - 过滤功能:负责对请求进行拦截,实现自定义的功能,例如说限流、熔断等等功能。
8.1 过滤器类型
Zuul 定义了 IZuulFilter 接口,定义了 Zuul 过滤器的两个基础方法。代码如下:
public interface IZuulFilter { |
Zuul 定义了 ZuulFilter 抽象基类,定义了 Zuul 过滤器的类型和执行顺序方法。代码如下:
public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> { |
过滤器一共分成四种类型,分别在不同阶段执行:
// FilterConstants.java |
这么说可能有点抽象,我们再来一起看一幅图和一段代码,即可清楚明白:
8.2 核心过滤器
Zuul 自带了很多 ZuulFilter 实现类,如下图所示:
友情提示:如下的过滤器,胖友先简单看看。后续可以进行调试,进一步深入了解。
① pre 过滤器
名称 | 优先级 | 作用 |
---|---|---|
ServletDetectionFilter | -3 | 检测当前请求是通过 DispatcherServlet 处理运行的还是 ZuulServlet 运行处理的。 |
Servlet30WrapperFilter | -2 | 对原始的HttpServletRequest 进行包装。 |
FormBodyWrapperFilter | -1 | 将 Content-Type为 application/x-www-form-urlencoded 或 multipart/form-data 的请求包装成 FormBodyRequestWrapper对 象。 |
② route 过滤器
名称 | 优先级 | 作用 |
---|---|---|
DebugFilter | 1 | 根据 zuul.debug.request 的配置来决定是否打印 debug 日志。 |
PreDecorationFilter | 5 | 对当前请求进行���处理以便执行后续操作。 |
RibbonRoutingFilter | 10 | 通过 Ribbon 和 Hystrix 来向服务实例发起请求,并将请求结果进行返回。 |
SimpleHostRoutingFilter | 100 | 只对请求上下文中有 routeHost 参数的进行处理,直接使用 HttpClient 向 routeHost 对应的物理地址进行转发。 |
SendForwardFilter | 500 | 只对请求上下文中有 forward.to 参数的进行处理,进行本地跳转。 |
③ post 过滤器
名称 | 优先级 | 作用 |
---|---|---|
SendErrorFilter | 0 | 当其他过滤器内部发生异常时的会由它来进行处理,产生错误响应。 |
SendResponseFilter | 1000 | 利用请求上下文的响应信息来组织请求成功的响应内容。 |
8.3 自定义过滤器
一般情况下,我们在 Zuul 上会去做的拓展,主要集中在 Filter 上,例如说接入认证服务。因此,我们来搭建 Zuul 自定义 Filter 实现的示例,提供“伪劣”的认证功能。还是老样子,从「3. 快速入门」小节的 labx-21-sc-zuul-demo01
项目,复制出本小节的 labx-21-sc-zuul-demo05-custom-zuul-filter
项目,最终项目结构如下图:
8.3.1 AuthZuulFilter
创建 AuthZuulFilter 类,认证过滤器,提供“伪劣”的认证功能。代码如下:
|
① 集成 ZuulFilter 抽象类,并在类上添加 @Component
注解,保证 Zuul 能够加载到 AuthZuulFilter 过滤器。
② <2.1>
和 <2.2>
处,定义了认证 Token 的 Header 名字 token
,和认证后的 UserId 的 Header 名字 user-id
。
<2.3>
处,定义了一个存储 token
和 userId
映射的 Map,毕竟咱仅仅是一个提供“伪劣”的认证功能的 Filter。
③ <3.1>
处,设置过滤器的类型为 pre
前置过滤器。
<3.2>
处,设置过滤器需要执行。
④ <4.1>
处,从请求 Header 中获取 token
,作为认证标识。
<4.2>
处,如果没有 token
,则不进行认证。因为可能是无需认证的 API 接口。
<4.3>
处,“伪劣”的认证逻辑,哈哈哈~实际场景下,一般调用远程的认证服务。
<4.4>
处,通过 token
获取不到 userId
,说明认证不通过,设置返回 401 状态码。
<4.5>
处,通过 token
获取到 userId
,说明认证通过,将 userId
添加到请求 Header,从而实现将 userId
传递给目标 URI。
友情提示:在 Spring Cloud Security 项目中,提供了 Zuul 的支持,胖友可以后续去愁一愁噢。
8.3.2 简单测试
① 执行 ZuulApplication 启动网关。
② 使用 Postman 模拟请求 Header token
为 yunai1
,演示认证不通过的情况,结果如下图:
③ 使用 Postman 模拟请求 Header token
为 yunai
,演示认证通过的情况,结果如下图:
8.4 禁用过滤器
在 Spring Cloud Zuul 中,我们可以通过在配置文件中,添加 zuul.<过滤器名>.<过滤器类型>.disable=true
配置项来禁用指定过滤器。
例如说,我们想要禁用 SendResponseFilter 后置过滤器,则可以添加 zuul.SendResponseFilter.post.disable=true
配置项来禁用。
9. 请求限流
Spring Cloud Zuul 并未提供请求限流功能。
友情提示:感兴趣的胖友,可以阅读《芋道 Spring Cloud 网关 Spring Cloud Gateway 入门》的「10. 请求限流」小节。
10. 基于 Hystrix 实现服务容错
示例代码对应仓库:
Zuul 作为服务网关,为了避免被调用的服务拖垮,在使用 Ribbon 调用后端服务时,集成 Hystrix 进行不同 Route 的隔离,进入实现服务的容错。具体的,后续胖友可以看看 AbstractRibbonCommand 的源码,如下图所示:
Hystrix 库,是 Netflix 开源的一个针对分布式系统的延迟和容错库。
Hystrix 供分布式系统使用,提供延迟和容错功能,隔离远程系统、访问和第三方程序库的访问点,防止级联失败,保证复杂的分布系统在面临不可避免的失败时,仍能有其弹性。
下面,我们来搭建 Zuul 基于 Hystrix 实现服务容错的使用示例。还是老样子,从「4. 基于注册中心实现动态路由」小节的 labx-21-sc-zuul-demo02-registry
项目,复制出本小节的 labx-21-sc-zuul-demo07-hystrix
项目,最终项目结构如下图:
分割线:先进行网关项目的改造。
10.1 引入依赖
因为 spring-cloud-starter-netflix-zuul
默认引入了 Hystrix 相关依赖,所以我们无需主动引入。如下图所示:
10.2 FallbackProvider
Spring Cloud Zuul 定义了 FallbackProvider 接口,提供服务调用失败的 Fallback 降级的响应,例如说 Hystrix Fallback。代码如下:
public interface FallbackProvider { |
#getRoute()
方法,该 FallbackProvider 匹配的 Route 编号,例如说route_users
。如果想要匹配所有 Route 则,返回*
。#fallbackResponse()
方法,处理指定route
路由发生的cause
异常,提供 Fallback 时的 ClientHttpResponse 响应。
这里,我们来实现一个 ApiFallbackProvider,响应结果为 {"code": 500, "message": "Service unavailable:${具体原因}"}
。代码如下:
|
- 代码比较简单,胖友自己瞅两眼就懂了。
10.3 搭建用户服务
创建 labx-21-sc-user-service
项目,作为 user-service
用户服务。代码比较简单,艿艿就不瞎哔哔了。最终项目如下图所示:
10.4 简单测试
① 执行 UserServiceApplication 启动 user-service
服务,执行 ZuulApplication 启动网关。
② 使用浏览器访问 http://127.0.0.1:8888/users/user/get?id=1 接口,成功调用 user-service
服务,并返回如下:
{ |
③ 停止 UserServiceApplication 关闭 user-service
服务,模拟服务不可用的情况。使用浏览器访问 http://127.0.0.1:8888/users/user/get?id=1 接口,失败调用 user-service
服务,并返回如下:
{ |
此时,我们快速使用浏览器访问 http://127.0.0.1:8888/users/user/get?id=1 接口,多次失败调用 user-service
服务,会触发 Hystrix 熔断,并返回如下:
{ |
11. 基于 Sentinel 实现服务容错
示例代码对应仓库:
labx-21-sc-zuul-demo07-sentinel
。
本小节我们来进行 Zuul 和 Sentinel 的整合,使用 Sentinel 进行 Zuul 的流量保护。
Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级流量控制产品,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。
Sentinel 提供了 sentinel-zuul-adapter
子项目,已经对 Zuul 进行适配,所以我们只要引入它,基本就完成了 Zuul 和 Sentinel 的整合,贼方便。
友情提示:本小节会引用《Sentinel 官方文档 —— 网关限流》的内容。
下面,我们来搭建 Gateway 基于 Sentinel 实现服务容错的使用示例。还是老样子,从「3. 快速入门」小节的 labx-21-sc-zuul-demo01
项目,复制出本小节的 labx-21-sc-zuul-demo07-sentinel
项目,最终项目结构如下图:
11.1 引入依赖
修改 pom.xml
文件,额外引入 Sentinel 相关的依赖如下:
<!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖,使用 Sentinel 提供服务保障,并实现对其的自动配置 --> |
11.2 配置文件
修改 application.yaml
配置文件,增加 Sentinel 的配置项。完整配置如下:
server: |
① 为了测试方便,我们修改 zuul.routes
配置项,所有请求都转发到艿艿的博客 http://www.iocoder.cn。
② spring.cloud.sentinel
配置项,是 Spring Cloud Sentinel 的配置项,后续胖友可以看看《芋道 Spring Cloud Alibaba 服务容错 Sentinel 入门》文章。
友情提示:艿艿本机搭建的 Sentinel 控制台启动在 7070 端口。
③ spring.cloud.sentinel.zuul
配置项,是 Sentinel 对 Zuul 的专属配置项,对应 SentinelZuulProperties 类。
order
:配置 Sentinel 拓展出的 Zuul 过滤器 SentinelZuulPreFilter、SentinelZuulPostFilter、SentinelZuulErrorFilter 的顺序。
11.3 ZuulBlockFallbackProvider
Sentinel 定义了 ZuulBlockFallbackProvider 接口,提供转发请求被 block 的 Fallback 降级的响应。代码如下:
public interface ZuulBlockFallbackProvider { |
#getRoute()
方法,该 ZuulBlockFallbackProvider 匹配的 Route 编号,例如说route_users
。如果想要匹配所有 Route 则,返回*
。#fallbackResponse()
方法,处理指定route
路由发生的cause
异常,提供 Fallback 时的 BlockResponse 响应。
Sentinel 提供了 ZuulBlockFallbackProvider 默认实现类 DefaultBlockFallbackProvider,提供默认响应,代码如下:
public class DefaultBlockFallbackProvider implements ZuulBlockFallbackProvider { |
这里,我们自定义 ZuulBlockFallbackProvider 实现类 CustomBlockFallbackProvider,提供自定义响应,代码如下:
|
- 注意
<X>
处,需要将自己注册到 ZuulBlockFallbackManager 管理器中。
11.4 ZuulApplication
修改 ZuulApplication 类的代码,声明这是一个 Zuul 应用。代码如下:
|
11.5 简单测试
① 执行 ZuulApplication 启动网关。
访问 Sentinel 控制台,可以看到网关已经成功注册上。如下图所示:
② 点击「流控规则」菜单,我们来给路由 yudaoyuanma
创建一个网关流控规则,如下图所示:
- 创建了针对路由
yudaoyuanma
的统一流控规则,允许每个 URL 的 QPS 上限为 3。
使用浏览器,快速访问 http://www.iocoder.cn 地址 4 次,会发现被 Sentinel 限流,返回 {"code":429, "message":"你被 Block 啦!", "route":"yudaoyuanma"}
结果。该文字提示,就是我们自定义的 CustomBlockFallbackProvider 提供的。
③ 下面,我们来给 /categories/**
路径,配置单独流控规则。
点击「API 管理」菜单,我们先创建一个包含 /categories/**
的 API 分组,如下图所示:
点击「流控规则」菜单,我们再给 API 分组创建一个网关流控规则,如下图所示:
- 创建了针对该 API 分组的单独流控规则,允许每个 URL 的 QPS 上限为 1。
友情提示:目前该功能,在 Zuul 的
servlet-path = /
时存在 BUG,具体可见 ISSUE#1109 的讨论。因此下面的演示,暂时只是艿艿期待的 YY 结果哈~
使用浏览器,快速访问 http://127.0.0.1:8888/categories/Spring-Cloud/ 地址 2 次,会发现被 Sentinel 限流,返回 {"code":429, "message":"你被 Block 啦!", "route":"yudaoyuanma"}
结果。
注意,虽然我们给 /categories/**
配置了单独的流控规则,但是通过路由配置的统一的流控规则也是生效的,也会作用到 /categories/**
上,即叠加的效果。
11.6 网关限流规则
sentinel-zuul-adapter
项目增加了网关限流规则(GatewayFlowRule),针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
GatewayFlowRule 的字段解释如下:
resource
:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。resourceMode
:规则是针对 API Gateway 的 route 还是用户在 Sentinel 中定义的 API 分组,默认是 route。grade
:限流指标维度,同限流规则的grade
字段。count
:限流阈值intervalSec
:统计时间窗口,单位是秒,默认是 1 秒。controlBehavior
:流量整形的控制效果,同限流规则的controlBehavior
字段,目前支持快速失败和匀速排队两种模式,默认是快速失败。burst
:应对突发请求时额外允许的请求数目。maxQueueingTimeoutMs
:匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。paramItem
:参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则。其中的字段:parseStrategy
:从请求中提取参数的策略,目前支持提取来源 IP、Host、任意 Header 和任意 URL 参数四种策略。fieldName
:若提取策略选择 Header 模式或 URL 参数模式,则需要指定对应的 header 名称或 URL 参数名称。pattern
:参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则统计该请求属性的所有值。matchStrategy
:参数值的匹配策略,目前支持精确匹配、子串匹配和正则匹配三种策略。
具体的示例,可以看看 sentinel-gw-flow.json
配置文件,内容如下:
[ |
11.7 API 定义分组
sentinel-zuul-adapter
项目增加了API 定义分组(ApiDefinition),用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫 my_api
,请求 path 模式为 /foo/**
和 /baz/**
的都归到 my_api
这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。
ApiDefinition 的字段解释如下:
apiName
:分组名。predicateItems
:匹配规则(ApiPathPredicateItem)数组。
具体的示例,可以看看 sentinel-gw-api-group.json
配置文件,内容如下:
[ |
12. 监控端点
示例代码对应仓库:
labx-21-sc-zuul-demo09-actuator
。
Spring Cloud Zuul 的 RoutesEndpoint 和 FiltersEndpoint 类,基于 Spring Boot Actuator,提供了自定义监控端点,实现了 Zuul 的监控管理的功能。整理如下表格:
路径 | 用途 |
---|---|
GET /routes |
获得所有路由(简要) |
GET /routes/details |
获得指定路由(明细) |
POST /routes |
发布 RoutesRefreshedEvent 事件 |
GET /filters |
获得所有过滤器 |
下面,我们来搭建 Zuul 的监控端点的使用示例。还是老样子,从「3. 快速入门」小节的 labx-08-sc-gateway-demo01
项目,复制出本小节的 labx-21-sc-zuul-demo09-actuator
项目,最终项目结构如下图:
12.1 引入依赖
因为 spring-cloud-starter-netflix-zuul
默认引入了 Spring Boot Actuator 相关依赖,所以我们无需主动引入。如下图所示:
12.2 配置文件
修改 application.yaml
配置文件,额外增加 Spring Boot Actuator 配置项。完成配置如下:
server: |
① 增加 management
配置项,设置的 Actuator 配置。每个配置项的作用,胖友看下艿艿添加的注释。如果还不理解的话,后续看下《芋道 Spring Boot 监控端点 Actuator 入门》文章。
下面,执行 ZuulApplication 启动网关,测试每一个端点。
12.3 路由端点
RoutesEndpoint 提供了 Zuul 路由相关的端点。
① GET
请求 http://127.0.0.1:18888/actuator/routes/ 地址,获得所有路由(简要)。结果如下:
{ |
② 请求 http://127.0.0.1:18888/actuator/routes/details 地址,获得所有路由(明细)。结果如下:
{ |
③ POST
请求 http://127.0.0.1:18888/actuator/routes/ 地址,发布 RoutesRefreshedEvent 并获得所有路由(简要)。结果如下:
{ |
12.4 过滤器端点
FiltersEndpoint 提供了 Zuul 过滤器相关的端点。
① GET
请求 http://127.0.0.1:18888/actuator/filters 地址,获取所有过滤器。结果如下图:
666. 彩蛋
至此,我们已经完成 Spring Cloud Zuul 的学习。如下是 Zuul 相关的官方文档:
更多关于 Zuul 的内容,胖友可以访问《Zuul 实现原理与源码解析系统 —— 精品合集》。