9. SpringCloud Alibaba Sentinel 流量控制、熔断降级、系统负载,热门规则的部署设置解说
@
目录
SpringCloud Alibaba Sentinel官方地址:https://github.com/alibaba/Sentinel
SpringCloud Alibaba Sentinel 快速开始: https://sentinelguard.io/zh-cn/docs/quick-start.html
SpringCloud Alibaba Sentinel官方中文地址: https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
SpringCloud Alibaba Sentinel 的使用手册:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_al%20ibaba_sentinel
1. Sentinel 是什么?
随着微服务的盛行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自顺应过载保护、热门流量防护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的焦点场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
- 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 的主要特性:
sentinel 可以完成的功能: 绿色方框列出的部分
Sentinel 的开源生态:
Sentinel 分为两个部分:
- 焦点库(Java 客户端)不依赖任何框架/库,能够运行于全部 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
一句话: Sentinel: 分布式系统的流量防卫兵, 保护你的微服务
Sentinel 焦点功能:
拿旅游景点举个示例,每个旅游景点通常都会有最大的欢迎量,不可能无穷制的放游 客进入,比如长城每天只卖八万张票,超过八万的游客,无法买票进入,因为假如超过 八万人,景点的工作职员可能就忙不过来,过于拥挤的景点也会影响游客的体验和心情, 并且还会有安全隐患;只卖 N 张票,这就是一种限流的手段
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,大概第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。假如依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
- 在调用系统的时候,假如调用链路中的某个资源出现了不稳定,最终会导致请求发生 堆积,如下图:
- 熔断降级可以解决这个问题,所谓的熔断降级就是当检测到调用链路中某个资源出现不 稳定的表现,例如请求响应时间长或非常比例升高的时候,则对这个资源的调用进行限 制,让请求快速失败,制止影响到其它的资源而导致级联故障。
Sentinel 系统自顺应保护从整体维度对应用入口流量进行控制,结合应用的 Load、总体平均 RT、入口 QPS 和线程数等几个维度的监控指标,让系统的入口流量和系统的负载达到一个均衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
根据系统能够处置惩罚的请求,和答应进来的请求,来做均衡,追求的目标是在系统不被 拖垮的情况下, 进步系统的吞吐率
某瞬时来了大流量的请求, 而假如此时要处置惩罚全部请求,很可能会导致系统负载过高, 影响稳定性。但其实可能反面几秒之内都没有消息投递,若直接把多余的消息丢掉则没 有充实利用系统处置惩罚消息的本领
Sentinel 的 Rate Limiter 模式能在某一段时间间隔内以匀速方式处置惩罚这样的请求, 充实利 用系统的处置惩罚本领, 也就是削峰填谷, 保证资源的稳定性
2. Sentinel 控制台
Sentinel 控制台官网说明地址:https://sentinelguard.io/zh-cn/docs/dashboard.html
Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。这里,我们将会详细讲述如何通过简单的步骤就可以使用这些功能。
接下来,我们将会逐一先容如何整合 Sentinel 焦点库和 Dashboard,让它发挥最大的作用。同时我们也在阿里云上提供企业级的 Sentinel 服务:AHAS Sentinel 控制台,您只需要几个简单的步骤,就能最直观地看到控制台如何实现这些功能,并体验多样化的监控及全自动托管的集群流控本领。
Sentinel 控制台包含如下功能:
- 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
- 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
- 规则管理和推送:统一管理推送规则。
- 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的现实情况进行定制。
注意:Sentinel 控制台目前仅支持单机部署。Sentinel 控制台项目提供 Sentinel 功能全集示例,不作为开箱即用的生产环境控制台,若盼望在生产环境使用请根据文档自行进行定制和改造。
3. Sentinel 下载&安装&运行
下载地址:https://github.com/alibaba/Sentinel/releases/tag/v1.8.0
运行:进入到对应 sentinel-dashoard-1.8.0 的目录当中,使用 cmd- java -jar sentinel-dashboard-1.8.0.jar
复制代码 注意: Sentinel 控制台 默认端口是 8080。
假如端口被占用了,我们可以更改 Sentinel 控制台的端口。- java -jar sentinel-dashboard-1.8.0.jar --server.port=9090
复制代码
访问:
浏览器: http://localhost:8080。注意:这里我的8080端口被占用了,所以这里我用的是9090端口。
浏览器输入: http://localhost:9090, 用户和密码都是 sentinel
登录成功后的页面, 目前是空的,因为 sentinel 还没有进行流量监控。
4. Sentinel 监控微服务
使用 Sentinel 控制台对 member-service-nacos-provider-10004 微服务 进行实时监控。
当调用了 member-service-nacos-provider-10004 微服务时 可以监控到请求的 url/QPS/ ,
响应时间/流量
使用 Sentinel 需要进入对应的 jar 包。如下-
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
- </dependency>
复制代码 在 application.yaml 文件当中配置 Sentinel 配置信息。如下图,所示:
- spring:
- application:
- name: member-service-nacos-provider # 配置应用的名称
- # 配置 nacos
- cloud:
- # 配置 Sentinel 的信息
- sentinel:
- transport:
- dashboard: localhost:9090 # 注意这里按照你自己配置的 sentinel 端口信息,默认是8080端口
- # 解读: spring.cloud.sentinel.transport.port
- # 1. spring.cloud.sentinel.transport.port 端口配置会在被监控的微服务当中
- # 对应的机器上启动一个 Http Server
- # 2. 该 Server 会 与 Sentinel 控制台做交互
- # 3. 比如: Sentinel 控制台添加了 1 个限流规则,会把规则数据 push 给这个
- # Http Server 接收,Http Server 再将规则注册到 Sentinel 当中
- # 简单的说明: spring.cloud.sentinel.transport.port: 指定被监控的微服务应用与
- # Sentinel 控制台交互的端口,微服务应用本地会起一个该端口占用的 Http Server
- port: 8719 # 默认的占用的端口是 8719,假如被占用了,会自动从 8719开始依次 + 1 进行一个扫描,
- # 直到找到为占用的端口,进行一个使用。
复制代码- server:
- port: 10004
- spring:
- application:
- name: member-service-nacos-provider # 配置应用的名称
- # 配置 nacos
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848 # 配置注册到哪个 Nacos Server的地址
- # 配置 Sentinel 的信息
- sentinel:
- transport:
- dashboard: localhost:9090 # 注意这里按照你自己配置的 sentinel 端口信息,默认是8080端口
- # 解读: spring.cloud.sentinel.transport.port
- # 1. spring.cloud.sentinel.transport.port 端口配置会在被监控的微服务当中
- # 对应的机器上启动一个 Http Server
- # 2. 该 Server 会 与 Sentinel 控制台做交互
- # 3. 比如: Sentinel 控制台添加了 1 个限流规则,会把规则数据 push 给这个
- # Http Server 接收,Http Server 再将规则注册到 Sentinel 当中
- # 简单的说明: spring.cloud.sentinel.transport.port: 指定被监控的微服务应用与
- # Sentinel 控制台交互的端口,微服务应用本地会起一个该端口占用的 Http Server
- port: 8719 # 默认的占用的端口是 8719,假如被占用了,会自动从 8719开始依次 + 1 进行一个扫描,
- # 直到找到为占用的端口,进行一个使用。
- datasource:
- type: com.alibaba.druid.pool.DruidDataSource # 配置 alibaba 的数据库连接池
- password: MySQL123
- username: root
- url: jdbc:mysql://localhost:3306/e_commerce_center_db?useSSL=true&useUnicode=true&characterEncoding=UTF-8
- mybatis:
- mapper-locations: classpath:mapper/*.xml # 指定 mapper.xml 文件位置 classpath 表示 resources 目录下
- type-aliases-package: com.rainbowsea.springcloud.entity # 实例 bean 类所在的包,这样可以通过类名的方式
- # 配置暴露所有的监控点:
- management:
- endpoints:
- web:
- exposure:
- include: '*'
复制代码 测试:
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
浏览器: localhost:10004/member/get/1
Sentinel 控制台监控页面,浏览器输入: http://localhost:10004/member/get/1
进入到 Sentinel 查看实时监控结果, http://localhost:8080/#/dashboard
注意事项和细节:
- QPS: Queries Per Second(每秒查询率),是服务器每秒响应的查询次数
- Sentinel 采取的是懒加载, 只有调用了某个接口/服务,才能看到监控数据。
5. Sentinel 流量控制
规则
对上图的解读:
- 资源名∶唯一名称,默认请求路径
- 针对来源∶Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来 源)
- 阈值类型/单机阈值∶
- QPS(每秒钟的请求数量)∶当调用该 api 的 QPS 达到阈值的时候,进行限流 线程数∶当调用该 api 的线程数达到阈值的时候,进行限流
解读 QPS 和线程数的区别, 注意听, 比如 QPS 和线程我们都设置阈值为 1:
- 对 QPS 而言,假如 1秒内,客户端发出了2次请求,就到达阈值,从而限流。 QPS 表示: 每秒钟的请求数量
- 对线程而言,假如在 1秒内,客户端降发出了 2 次请求,不一定达到线程限制的阈值,为什么呢,假设我们 1次请求背景会创建一个线程,但是这个请求完成时间是 0.1秒(可以视为该请求对应的线程存活 0.1 秒),
所以当客户端第2次请求时,(比如客户端是在 0.3 秒发出的),这时第1个请求的线程就是已经结束了,因此就没有达到线程的阈值,也不会
限流。
- 可以这样理解,假如 1 个请求对应的线程平均实验时间为 0.1 那么,就相当于 QPS 为 10
是否集群:不需要集群。
5.1 流量控制实例-QPS
演示: 当调用 member-service-nacos-provider-10004 的 /member/get/ 接口/API 时,限制 1 秒内最多访问 1 次,否则直接失败,抛非常.
配置实现步骤:
测试
启动 Nacos Server 8848
启动 Sentinel909控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
浏览器: localhost:10004/member/get/1
Sentinel 控制台监控页面:
浏览器输入: http://localhost:10004/member/get/1 , 1 秒钟内访问次数不超过 1 次, 页 面显示正常
浏览器输入: http://localhost:10004/member/get/1 , 1 秒钟内访问次数超过 1 次, 页面 出现错误提示
注意事项和细节:
- 流量规则改动,实时生效,不需重启微服务 , Sentine 控制台
- 在 sentinel 配 置 流 量 规 则 时 , 如 何 配 置 通 配 符 问 题 , 比 如 /member/get/1 和/member/get/2 统一使用一个规则
方案一 :在sentinel中 /member/get?id=1 和 /member/get?id=2 被统一认为是 /member/get 所以只要对/member/get 限流就OK了.
- 方案1: 在 sentinel 中/member/get?id=1 和 /member/get?id=2 被统一认为是 /member/get 所以
- 只要对 /member/get 限流就OK了。
- /**
- * 这里我们使用 url占位符 + @PathVariable
- *
- * @param id
- * @return
- */
- //@GetMapping("/member/get/{id}")
- // 在sentinel中 /member/get?id=1 和 /member/get?id=2 被统一认为是 /member/get 所以只要对/member/get 限流就OK了. 进行统一的限流
- @RequestMapping(value = "/member/get/", params = "id", method = RequestMethod.GET)
- //public Result getMemberById(@PathVariable("id") Long id, HttpServletRequest request) {
- public Result getParameter(Long id) {
- Member member = memberService.queryMemberById(id);
- //String color = request.getParameter("color");
- //String age = request.getParameter("age");
- // 模拟超时 ,这里暂停 5秒
- /* try {
- TimeUnit.SECONDS.sleep(5);
- } catch (Exception e) {
- e.printStackTrace();
- }*/
- // 使用 Result 把查询到的结果返回
- if (member != null) {
- //return Result.success("查询会员成功 member-service-nacos-provider-10004 color" + color + "age" + age, member);
- return Result.success("查询会员成功 member-service-nacos-provider-10004 color",member);
- } else {
- return Result.error("402", "ID" + id + "不存在 member-service-nacos-provider-10004 ");
- }
- }
复制代码
方案二: URL 资源洗濯
可以通过 UrlCleaner 接口来实现资源洗濯,也就是对于 /member/get/{id} 这个 URL,
我们可以统一归集到 /member/get/* 资源下,具体的代码实现如下: 需要实现 UrlCleaner接口
并重写此中的 clean 方法即可。
- package com.rainbowsea.springcloud.controller;
- import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.stereotype.Component;
- /**
- * 方案2: URL 资源清洗
- * 可以通过 UrlCleaner 接口来实现资源清洗,也就是对于 /member/get/{id} 这个 URL,
- * 我们可以统一归集到 /member/get/* 资源下,具体的代码实现如下: 需要实现 UrlCleaner接口
- * 并重写其中的 clean 方法即可
- */
- @Component // 注意:同样要被 Spring IOC 容器管理起来
- public class CustomUrlCleaner implements UrlCleaner {
- @Override
- public String clean(String originUrl) {
- // 判断字符串是否为空 Null
- // 特别注意: StringUtils.isBlank 是在:org.apache.commons.lang3.StringUtils 包下的
- if (StringUtils.isBlank(originUrl)) {
- return originUrl;
- }
- if (originUrl.startsWith("/member/get")) {
- // 1.如果请求的是接口 /member/get 开头的,比如: /member/get/1
- // 2.给sentinel 的返回的资源名就是 /member/get/*
- // 3. 在 sentinel 对 /member/get/* 添加流控规则即可
- return "/member/get/*";
- }
- return originUrl;
- }
- }
复制代码 5.2 流量控制实例-线程数
通过 Sentinel 实现 流量控制
当调用 member-service-nacos-provider-10004 的 /member/get/* 接口/API 时,限制 只有一个工作线程,否则直接失败,抛非常.
配置实现步骤:
测试:
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
浏览器: localhost:10004/member/get/1
结果页面:
浏览器输入: http://localhost:10004/member/get/1 , 快速刷新, 页面显示正常(缘故原由 是服务实验时间很短,刷新下一次的时候,启动的工作线程,已经完成)
为了看到结果,我们修改下 com/rainbowsea/springcloud/controller/MemberController.java。模拟延时。
- // 让线程休眠1s,模拟执行时间
- try {
- TimeUnit.MILLISECONDS.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
复制代码- @GetMapping("/member/get/{id}")
- public Result getMemberById(@PathVariable("id") Long id) {
- Member member = memberService.queryMemberById(id);
- //String color = request.getParameter("color");
- //String age = request.getParameter("age");
- // 让线程休眠1s,模拟执行时间
- try {
- TimeUnit.MILLISECONDS.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(" enter getMemberById... 当前线程id = " + Thread.currentThread().getId() + "时间 = " + new Date());
- // 使用 Result 把查询到的结果返回
- if (member != null) {
- //return Result.success("查询会员成功 member-service-nacos-provider-10004 color" + color + "age" + age, member);
- return Result.success("查询会员成功 member-service-nacos-provider-10004 color" + member);
- //return Result.success("查询会员成功 member-service-nacos-provider-10004 color",member);
- } else {
- return Result.error("402", "ID" + id + "不存在 member-service-nacos-provider-10004 ");
- }
- }
复制代码 重启 member-service-nacos-provider-10004 , 注意需要重新加入流控规则.
浏览器输入: http://localhost:10004/member/get/1 , 快速刷新 页面出现非常.
注意事项和细节:
- 当我们请求一次微服务的 API 接口时,背景会启动一个线程 [演示下]
阈值类型 QPS 和 线程数的区别讨论
假如一个线程平均实验时间 为 0.05 秒,就说明在 1秒钟,可以实验 20次(相当于 QPS 为 20)
假如一个线程平均实验时间 为 1秒,说明 1 秒钟,可以实验 1次数(相当于 QPS 为1)
假如一个线程平均实验时间 为 2 秒,说明2秒钟内,才能实验1次请求。
5.3 流量控制实例-关联
关联的含义: 当关联的资源达到阈值时,就限流自己
通过 Sentinel 实现 流量控制
当调用 member-service-nacos-provider-10004 的 /t2 API 接口时,假如 QPS 超过 1,这 时调用 /t1 API 接口 直接接失败,抛非常. 老师梳理 /t2 是关联的资源 , 限流的资源是 /t1
这里使用 postman 模拟高并发访问/t2 ;然后在 postman 实验高并发访问/t2没有结束时,去访问 /t1 才能看到流控非常出现。t1被流控了
配置实现步骤
测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
Postman 模拟高并发访问/t2
创建新的 http request
保存 request 到 一个新的 collection 中
设置 run collection 参数, 并运行
浏览器访问: http://localhost:10004/t1
注意事项和细节:
在 postman 实验 高并发访问 /t2 没有结束时, 去访问 /t1 才能看到流控非常出现。
5.4 流量控制实例-Warm up
Warm up 先容:
- 当流量突然增大的时候,我们常常会盼望系统从空闲状态到繁忙状态的切换的时间长 一些。即假如系统在此之前长期处于空闲的状态,我们盼望处置惩罚请求的数量是缓步的 增多,经过预期的时间以后,到达系统处置惩罚请求个数的最大值。Warm Up(冷启动, 预热)模式就是为了实现这个目标的。
- 这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等
- 如图:
通常冷启动的过程系统答应通过的 QPS 曲线图(上图)
- 默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长渐渐升至设定的 QPS 阈 值
Warm up 称为 冷启动/预热
应用场景: 秒杀在开启瞬间,大流量很轻易造成冲毁系统,Warmup 可逐步的把流量放入,最 终将阀值增长到设置阀值。
通过 Sentinel 实现 流量控制,演示 Warm up
- 调用 member-service-nacos-provider-10004 的 /t2 API 接口,将 QPS 设置为 9, 设置 Warm up 值为 3
- 含义为 请求 /t2 的 QPS 从 threshold / 3( 9 /3 = 3) 开始,经预热时长(3 秒)渐渐升至 设定的 QPS 阈值(9)
- 为什么是 9 / 3, 这个是 3 就是默认冷启动启动加载因子 coldFactor=3
- 测试预期结果: 在前 3 秒,假如访问 /t2 的 QPS 超过 3, 会直接报错,在 3 秒后 访问 /t2 的 QPS 超过 3, 小于等于 9, 是正常访问
配置实现步骤:
测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
浏览器: localhost:10004/t2
浏览器访问 http://localhost:10004/t2 快速刷新页面,在前 3 秒,会出现流控非常, 后 3 秒就正常了(假如你刷新非常快 QPS>9 , 仍然会出现流控非常)
注意事项和细节:
- 测试 Warm up 结果不是很好测,假如出不来可以尝试,调整 流控规则: 比如 QPS 为 11, Warm up 预热时间 6 秒
- 假如请求停止(即: 一段时间没有达到阈值), Warm up 过程将重复, 小伙伴可以理解是一个弹 性过程
5.5 流量控制实例-排队
排队方式:这种方式严格控制了请求通过的间隔时间,也即是让请求以匀称的速度通 过,对应的是漏桶算法。
这种方式主要用于处置惩罚间隔性突发的流量,例如消息队列。比如这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们盼望系统能够在接下来的 空闲期间渐渐处置惩罚这些请求,而不是在第一秒直接拒绝多余的请求。 类似前面说的 削峰填谷。
匀速排队,阈值必须设置为 QPS
通过 Sentinel 实现 流量控制-排队
- 调用 member-service-nacos-provider-10004 的 /t2 API 接口,将 QPS 设置为 1
- 当调用 /t2 的 QPS 超过 1 时,不拒绝请求,而是排队等待, 依次实验
- 当等待时间超过 10 秒,则为等待超时.
为了测试看到结果,修改 com/rainbowsea/springcloud/controller/MemberController.java- @GetMapping("/t2")
- public Result t2() {
- // 让线程休眠 1s,模拟执行时间为1s=>当多少个请求就会造成超时
- try {
- TimeUnit.MILLISECONDS.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- // 输出线程信息
- log.info("执行t2(),线程id={}",Thread.currentThread().getId());
- return Result.success("t2执行成功");
- }
复制代码 配置实现步骤:
测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
浏览器: localhost:10004/t2
- 输出结果分析
- 没有报错误
- 背景请求排队实验,每隔1s 匀速实验
浏览器访问 http://localhost:10004/t2 快速刷新页面 20 次,当请求等待时间超过 10S, 仍然出现流控非常
6. Sentinel 熔断降级
线程堆积引出熔断降级
- 一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,大概第三方 API 等[架构图]。
- 例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需 要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。假如依赖的服务出现 了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用
- 这时,我们对不稳定的服务进行熔断降级,让其快速返回结果,不要造成线程堆积
- 文档地址: https://sentinelguard.io/zh-cn/docs/circuit-breaking.html
- 现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成 复杂的调用链路。
- 链路调用中会产生放大的结果。复杂链路上的某一环不稳定,就可能会层层级联,最 终导致整个链路都不可用。
- 因此需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,制止局部 不稳定因素导致整体的雪崩
熔断 降级 限流三者的关系:
- 熔断夸大的是服务之间的调用能实现自我规复的状态
- 限流是从系统的流量入口思量, 从进入的流量上进行限制 达到保护系统的作用。
- 降级, 是从系统业务的维度思量,流量大了大概频仍非常, 可以牺牲一些非焦点业务,保 护焦点流程正常使用。
梳理:
- 熔断是降级方式的一种
- 降级又是限流的一种方式
- 三者都是为了通过一定的方式在流量过大大概出现非常时, 保护系统的手段
6.1 熔断策略
慢调用比例:
- 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置答应的 慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用
- 当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的 比例大于阈值,则接下来的熔断时长内请求会自动被熔断
- 熔断时长后, 熔断器会进入探测规复状态(HALF-OPEN 状态),若接下来的一个请求响 应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断
- 配置参考:
非常比例:
- 非常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最 小请求数目,并且非常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断
- 经过熔断时长后熔断器会进入探测规复状态(HALF-OPEN 状态)
- 若接下来的一个请求成功完成(没有错误)则结束熔断 否则会再次被熔断
- 非常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%
- 配置参数
工作示图:
非常数:
- 非常数 (ERROR_COUNT):当单位统计时长内的非常数目超过阈值之后会自动进行熔 断
- 经过熔断时长后熔断器会进入探测规复状态(HALF-OPEN 状态)
- 若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断
- 配置参考:
6.2 熔断降级实例-慢调用比例
- 需求: 通过 Sentinel 实现 熔断降级控制-慢调用比例
- 当调用 member-service-nacos-provider-10004 的 /t3 API 接口时,假如在 1s 内持续进 入了 5 个请求,并且请求的平均响应时间超过 200ms, 那么就在未来 10 秒钟内,断路器 打开, 让 /t3 API 接口 微服务不可用
- 反面对/t3 API 接口 访问降到 1S 内 1 个请求,降低访问量了,断路器关闭,微服务规复
- 修改 com/rainbowsea/springcloud/controller/MemberController.java 增长方法 t3()
为/t3 增长降级规则
测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
Postman 测试
- 先创建 collection , 也可以在已经存在的 collection 进行修改。
点击 Run sentinel
浏览器访问: http://localhost:10004/t3
停止 Postman
浏览器访问: http://localhost:10004/t3 , 结果正常了(需要在停止 Postman 10s 后)
注意事项和细节:
- 平均响应时间 超出阈值 且 在 1s 内通过的请求>=5, 两个条件同时满意后触发降级
- 熔断时间过后,关闭断路器,访问规复正常。
6.3 熔断降级实例-非常比例
通过 Sentinel 实现 熔断降级控制
- 当调用 member-service-nacos-provider-10004 的 /t4 API 接口时,当资源的每秒请求 量>=5,并且每秒非常总数占通过量的比值超过 20%(即非常比例到 20%), 断路器打开(即: 进入降级状态), 让 /t4 API 接口 微服务不可用
- 当对/t4 API 接口 访问降到 1S 内 1 个请求,降低访问量了,断路器关闭,5 秒后微服务规复。
- 修改 com/rainbowsea/springcloud/controller/MemberController.java 增长方法 t4()
配置实现步骤
测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
Postman 测试:
- 先创建给 collection , 也可以在已经存在的 collection 进行修改, 一定确保更新成功.
注意事项和细节
- 当资源的每秒请求量>=5,并且每秒非常总数占通过量的比值超过阈值,资源进入降级状态, 需 要两个条件都满意
- 测试时,假如熔断降级和规复服务两个状态切换不明显,将时间窗口值调整大一点比如 60, 就 OK 了
6.4 熔断降级实例-非常数
- 需求: 通过 Sentinel 实现 熔断降级控制
- 当调用 member-service-nacos-provider-10004 的 /t5 API 接口时,当资源的每分钟请 求量>=5,并且每分钟非常总数>=5 , 断路器打开(即: 进入降级状态), 让 /t5 API 接口 微服务不可用
- 当熔断时间(比如 20S)结束后,断路器关闭, 微服务规复.
- 修改 com/rainbowsea/springcloud/controller/MemberController.java 增长方法 t5( )
配置实现步骤
测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
浏览器: http://localhost:10004/t5
http://localhost:10004/t5 , 访问 5 次,出现 5 次非常(1 分钟内完成)
5 次非常后,出现熔断降级
20S 后,再次访问 http://localhost:10004/t5, 返回正常结果了
注意事项和细节
- 资源在 1 分钟的非常数目超过阈值之后会进行熔断降级
- 非常数统计是分钟级别的,若 设置的时间窗口 小于 60s,则结束熔断状态后仍可能再进入熔 断状态, 测试时,最好将时间窗口设置超过 60S
7. Sentinel 热门规则
一个问题引出热门 key 限流
7.1 热门 Key 限流-实例
需求: 通过 Sentinel 实现 热门 Key 限流
- 对 member-service-nacos-provider-10004 的 /news?id=x&type=x API 接口进行热门限 流
- 假定 id=10 这一条新闻是当前的热门新闻, 当查询新闻时,对通常的 id(非热门新闻) 请求 QPS 限定为 2, 假如 id=10 QPS 限定为 100
- 假如访问超出了规定的 QPS, 触发热门限流机制, 调用自定义的方法,给出提示信息.
- 当对 /news?id=x&type=x API 接口 降低访问量,QPS 达到规定范围, 服务规复
- 修 改 com/rainbowsea/springcloud/controller/MemberController.java 增 加 方 法 queryNews()
测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard 10.6.4.3.3
启动 member-service-nacos-provider-10004
配置步骤:
- 为资源 news 增长热门规则, 注意不是 /news
浏览器: http://localhost:10004/news?id=1&type=教育
独立设置热门 id=10 的 QPS 阈值(即添加破例)
- 独立设置热门 id=10 的 QPS 阈值(即添加破例)
浏览器: http://localhost:10004/news?id=10&type=教育
- 浏览器访问的 id 不是 10 的,仍然服从 QPS 不能超过 2 的热门限制
注意事项和细节
- 热门参数类型是(byte/int/long/float/double/char/String)
- 热门参数值,可以配置多个
- 热门规则只对指定的参数生效 (比如本实例对 id 生效, 对 type 不生效)
8. 系统规则
一个问题引出系统规则
- 如我们系统最大性能能抗 100QPS, 如何分配 /t1 /t2 的 QPS?
- 方案 1: /t1 分配 QPS=50 /t2 分配 QPS=50 , 问题, 假如/t1 当前 QPS 达到 50 , 而 /t2 的 QPS 才 10, 会造成没有充实利用服务器性能.
- 方案 2: /t1 分配 QPS=100 /t2 分配 QPS=100 , 问题, 轻易造成 系统没有流量保护, 造成请求线程堆积,形成雪崩.
- 有没有对各个 资源请求的 QPS 弹性设置, 只要总数不超过系统最大 QPS 的流量保护规 则? ==> 系统规则
- 文档地址:
- 一句话: 系统规则作用, 在系统稳定的条件下,保持系统的吞吐量
图示解读:
- 系统处置惩罚请求的过程想象为一个水管,到来的请求是往这个水管灌水,当系统处置惩罚顺 畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT是最短的;
- 反之,当请求堆积的时候,那么处置惩罚请求的时间则会变为:排队时间 + 最短处置惩罚时间
系统规则:
- Load 自顺应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自 顺应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算 的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。
- CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比力灵敏。
- 平均 RT:当单台机器上全部入口流量的平均 RT 达到阈值即触发系统保护,单位是毫 秒。
- 并发线程数:当单台机器上全部入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上全部入口流量的 QPS 达到阈值即触发系统保护。
需求: 通过 Sentinel 实现 系统规则-入口 QPS
- 对 member-service-nacos-provider-10004 的 全部 API 接口进行流量保护,不管访问 哪个 API 接口, 系统入口总的 QPS 不能大于 2, 大于 2,就进行限流控制
- 提示: 上面的 QPS 是老师为了方便看结果, 设置的很小
配置实现步骤:
测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
浏览器: http://localhost:10004/t1
9. @SentinelResource 自定义全局限流处置惩罚类
@SentinelResource 自定义全局限流处置惩罚类
观察如下代码:
说明: 当配置的资源名 news 触发限流机制时,会调用 newsBlockHandler 方法
- 每个@SentinelResource 对应一个非常处置惩罚方法,会造成方法很多
- 非常处置惩罚方法和资源请求方法在一起,不利于业务逻辑的分离
- 解决方案-> 自定义全局限流处置惩罚类.
- 需求: 请编写一个自定义全局限流处置惩罚类,完成对非常处置惩罚.
创建 com/rainbowsea/springcloud/handler/CustomGlobalBlockHandler.java
- package com.rainbowsea.springcloud.handler;
- import com.alibaba.csp.sentinel.slots.block.BlockException;
- import com.rainbowsea.springcloud.entity.Result;
- /**
- * 1. CustomGlobalBlockHandler : 全局限流处理类
- * 2. 在 CustomGlobalBlockHandler 类中,可以编写限流处理方法,但是要求方法是static
- */
- public class CustomGlobalBlockHandler {
- public static Result handlerMethod1(BlockException blockException) {
- return Result.error("400", "客户自定义异常/限流处理方法handlerMethod1");
- }
- public static Result handlerMethod2(BlockException blockException) {
- return Result.error("401", "客户自定义异常/限流处理方法handlerMethod2");
- }
- }
复制代码 修改 com/rainbowsea/springcloud/controller/MemberController.java 增长方法 t6()
- private static int num = 0; // 执行的计数器-static静态
- // 这里我们使用全局限流处理类,显示限流信息
- /**
- * value="t6" 表示 sentinel 限流资源的名字
- * blockHandlerClass = CustomGlobalBlockHandler.class:全局限流处理类
- * blockHandler = "handlerMethod1" 指定使用全局限流处理类哪个方法,来处理限流信息
- * fallbackClass = CustomGlobalFallbackHandler.class 全局 fallback处理类
- * fallback = "fallbackHandlerMethod1" 指定使用全局fallback处理类哪个方法来处理java异常/业务异常
- * exceptionsToIgnore = {NullPointerException.class}
- *
- * @return
- */
- @GetMapping("/t6")
- @SentinelResource(value = "t6",
- // //设置处理sentinel 控制台违规后的异常 blockHand
- blockHandlerClass = CustomGlobalBlockHandler.class,
- blockHandler = "handlerMethod1",
- //设置处理Java异常的 fallback
- fallbackClass = CustomGlobalFallbackHandler.class,
- fallback = "fallbackHandlerMethod1",
- // 如果希望忽略某个异常,可以使用 exceptionsToIgnore,这里忽略NullPointerException异常
- exceptionsToIgnore = {NullPointerException.class}
- )
- public Result t6() {
- log.info("执行t6() 线程id={}", Thread.currentThread().getId());
- // 假定;当访问t6资源次数是5倍数时,就出现Java异常
- if (++num % 5 == 0) {
- throw new NullPointerException("null 指针异常 num=" + num);
- }
- if (++num % 6 == 0) {
- throw new RuntimeException("RuntimeException num=" + num);
- }
- return Result.success("200", "t6()执行OK~~~");
- }
复制代码 配置实现步骤
为资源 /t6 增长流控规则,方便测试
测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
浏览器: http://localhost:10004/t6
9.1 @ fallback
看一段代码-引出 fallback
修 改 member-service-nacos-provider-10004 com/rainbowsea/springcloud/controller/MemberController.java 增长一段代码.
 - private static int num = 0; // 执行的计数器-static静态
- // 这里我们使用全局限流处理类,显示限流信息
- /**
- * value="t6" 表示 sentinel 限流资源的名字
- * blockHandlerClass = CustomGlobalBlockHandler.class:全局限流处理类
- * blockHandler = "handlerMethod1" 指定使用全局限流处理类哪个方法,来处理限流信息
- * fallbackClass = CustomGlobalFallbackHandler.class 全局 fallback处理类
- * fallback = "fallbackHandlerMethod1" 指定使用全局fallback处理类哪个方法来处理java异常/业务异常
- * exceptionsToIgnore = {NullPointerException.class}
- *
- * @return
- */
- @GetMapping("/t6")
- @SentinelResource(value = "t6",
- // //设置处理sentinel 控制台违规后的异常 blockHand
- blockHandlerClass = CustomGlobalBlockHandler.class,
- blockHandler = "handlerMethod1",
- //设置处理Java异常的 fallback
- fallbackClass = CustomGlobalFallbackHandler.class,
- fallback = "fallbackHandlerMethod1",
- // 如果希望忽略某个异常,可以使用 exceptionsToIgnore,这里忽略NullPointerException异常
- exceptionsToIgnore = {NullPointerException.class}
- )
- public Result t6() {
- log.info("执行t6() 线程id={}", Thread.currentThread().getId());
- // 假定;当访问t6资源次数是5倍数时,就出现Java异常
- if (++num % 5 == 0) {
- throw new NullPointerException("null 指针异常 num=" + num);
- }
- if (++num % 6 == 0) {
- throw new RuntimeException("RuntimeException num=" + num);
- }
- return Result.success("200", "t6()执行OK~~~");
- }
复制代码 浏览器: http://localhost:10004/t6 , 看结果当 num 为 5 的整数时,返回的是 error 页面, 不友爱.
怎么解决=> 使用 fallback
fallback 基本先容
blockHandler 只负责 sentine 控制台配置违规
fallback 负责 Java 非常/业务非常
需求: 请编写一个自定义全局 fallback 处置惩罚类, 处置惩罚 java 非常/业务非常 。也就是解决前面我们提出的问题解
代码实现:
- 在 member-service-nacos-provider-10004 创建com/hspedu/springcloud/handler/CustomGlobalFallbackHandler.java
- package com.rainbowsea.springcloud.handler;
- import com.rainbowsea.springcloud.entity.Result;
- /**
- * CustomGlobalFallbackHandler :全局 fallback处理类
- * 在 CustomGlobalFallbackHandler 类中,可以去编写处理Java异常/业务异常方法-static
- */
- public class CustomGlobalFallbackHandler {
- public static Result fallbackHandlerMethod1(Throwable throwable) {
- return Result.error("402", "java异常信息 + " + throwable.getMessage());
- }
- public static Result fallbackHandlerMethod2(Throwable throwable) {
- return Result.error("402", "java异常信息 + " + throwable.getMessage());
- }
- }
复制代码 在 member-service-nacos-provider-10004 修改 com/hspedu/springcloud/controller/MemberController.java
 - // 这里我们使用全局限流处理类,显示限流信息
- /**
- * value="t6" 表示 sentinel 限流资源的名字
- * blockHandlerClass = CustomGlobalBlockHandler.class:全局限流处理类
- * blockHandler = "handlerMethod1" 指定使用全局限流处理类哪个方法,来处理限流信息
- * fallbackClass = CustomGlobalFallbackHandler.class 全局 fallback处理类
- * fallback = "fallbackHandlerMethod1" 指定使用全局fallback处理类哪个方法来处理java异常/业务异常
- * exceptionsToIgnore = {NullPointerException.class}
- *
- * @return
- */
- @GetMapping("/t6")
- @SentinelResource(value = "t6",
- // //设置处理sentinel 控制台违规后的异常 blockHand
- blockHandlerClass = CustomGlobalBlockHandler.class,
- blockHandler = "handlerMethod1",
- //设置处理Java异常的 fallback
- fallbackClass = CustomGlobalFallbackHandler.class,
- fallback = "fallbackHandlerMethod1",
- // 如果希望忽略某个异常,可以使用 exceptionsToIgnore,这里忽略NullPointerException异常
- exceptionsToIgnore = {NullPointerException.class}
- )
- public Result t6() {
- log.info("执行t6() 线程id={}", Thread.currentThread().getId());
- // 假定;当访问t6资源次数是5倍数时,就出现Java异常
- if (++num % 5 == 0) {
- throw new NullPointerException("null 指针异常 num=" + num);
- }
- if (++num % 6 == 0) {
- throw new RuntimeException("RuntimeException num=" + num);
- }
- return Result.success("200", "t6()执行OK~~~");
- }
复制代码 测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
浏览器: http://localhost:10004/t6
浏览器输入: http://localhost:10004/t6 , 访问次数不是 5 的倍数, 返回正常结果
浏览器输入: http://localhost:10004/t6 , 访问次数是 5 的倍数 返回 fallback 指定方法
,信息
为资源 /t6 增长流控规则,方便测试
在流控规则菜单,可以看到新增规则
浏览器输入: http://localhost:10004/t6 , 假如访问 QPS 大于 1 , 由 blockHandler 指 定的方法处置惩罚,访问次数是 5 的倍数, 由 fallback 指定方法处置惩罚, 其它情况返回正常的结果.
9.2 @exceptionsToIgnore 忽略
假如盼望忽略某个非常,可以使用 exceptionsToIgnore
- 假如盼望忽略某个非常(支持数组),可以使用 exceptionsToIgnore
10. 补充: 接入 Sentinel 的方式
文档地址: https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
基本使用:
注 解 方 式 埋 点 不 支 持 private 方 法 https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
11. 末了:
“在这个末了的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的范畴奋斗。感谢你们,我们总会在某个时刻再次相遇。”
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |