@
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
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
Sentinel 的主要特性:
sentinel 可以完成的功能: 绿色方框列出的部分
Sentinel 的开源生态:
Sentinel 分为两个部分:
一句话: Sentinel: 分布式系统的流量防卫兵, 保护你的微服务
Sentinel 核心功能:
拿旅游景点举个示例,每个旅游景点通常都会有最大的接待量,不可能无限制的放游 客进入,比如长城每天只卖八万张票,超过八万的游客,无法买票进入,因为如果超过 八万人,景点的工作人员可能就忙不过来,过于拥挤的景点也会影响游客的体验和心情, 并且还会有安全隐患;只卖 N 张票,这就是一种限流的手段
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
- 在调用系统的时候,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生 堆积,如下图:
- 熔断降级可以解决这个问题,所谓的熔断降级就是当检测到调用链路中某个资源出现不 稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限 制,让请求快速失败,避免影响到其它的资源而导致级联故障。
Sentinel 系统自适应保护从整体维度对应用入口流量进行控制,结合应用的 Load、总体平均 RT、入口 QPS 和线程数等几个维度的监控指标,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
根据系统能够处理的请求,和允许进来的请求,来做平衡,追求的目标是在系统不被 拖垮的情况下, 提高系统的吞吐率
某瞬时来了大流量的请求, 而如果此时要处理所有请求,很可能会导致系统负载过高, 影响稳定性。但其实可能后面几秒之内都没有消息投递,若直接把多余的消息丢掉则没 有充分利用系统处理消息的能力
Sentinel 的 Rate Limiter 模式能在某一段时间间隔内以匀速方式处理这样的请求, 充分利 用系统的处理能力, 也就是削峰填谷, 保证资源的稳定性
Sentinel 控制台官网说明地址:https://sentinelguard.io/zh-cn/docs/dashboard.html
Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。这里,我们将会详细讲述如何通过简单的步骤就可以使用这些功能。
接下来,我们将会逐一介绍如何整合 Sentinel 核心库和 Dashboard,让它发挥最大的作用。同时我们也在阿里云上提供企业级的 Sentinel 服务:AHAS Sentinel 控制台,您只需要几个简单的步骤,就能最直观地看到控制台如何实现这些功能,并体验多样化的监控及全自动托管的集群流控能力。
Sentinel 控制台包含如下功能:
注意:Sentinel 控制台目前仅支持单机部署。Sentinel 控制台项目提供 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 还没有进行流量监控。
使用 Sentinel 控制台对 member-service-nacos-provider-10004 微服务 进行实时监控。
当调用了 member-service-nacos-provider-10004 微服务时 可以监控到请求的 url/QPS/ ,
响应时间/流量
使用 Sentinel 需要进入对应的 jar 包。如下
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
在 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 和线程数的区别, 注意听, 比如 QPS 和线程我们都设置阈值为 1:
- 对 QPS 而言,如果 1秒内,客户端发出了2次请求,就到达阈值,从而限流。 QPS 表示: 每秒钟的请求数量
- 对线程而言,如果在 1秒内,客户端降发出了 2 次请求,不一定达到线程限制的阈值,为什么呢,假设我们 1次请求后台会创建一个线程,但是这个请求完成时间是 0.1秒(可以视为该请求对应的线程存活 0.1 秒),
所以当客户端第2次请求时,(比如客户端是在 0.3 秒发出的),这时第1个请求的线程就是已经结束了,因此就没有达到线程的阈值,也不会
限流。- 可以这样理解,如果 1 个请求对应的线程平均执行时间为 0.1 那么,就相当于 QPS 为 10
是否集群:不需要集群。
演示: 当调用 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 次, 页面 出现错误提示
注意事项和细节:
方案一 :在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; } }
通过 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 , 快速刷新 页面出现异常.
注意事项和细节:
阈值类型 QPS 和 线程数的区别讨论
如果一个线程平均执行时间 为 0.05 秒,就说明在 1秒钟,可以执行 20次(相当于 QPS 为 20)
如果一个线程平均执行时间 为 1秒,说明 1 秒钟,可以执行 1次数(相当于 QPS 为1)
如果一个线程平均执行时间 为 2 秒,说明2秒钟内,才能执行1次请求。
关联的含义: 当关联的资源达到阈值时,就限流自己
通过 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 才能看到流控异常出现。
Warm up 介绍:
通常冷启动的过程系统允许通过的 QPS 曲线图(上图)
Warm up 称为 冷启动/预热
应用场景: 秒杀在开启瞬间,大流量很容易造成冲垮系统,Warmup 可慢慢的把流量放入,最 终将阀值增长到设置阀值。
通过 Sentinel 实现 流量控制,演示 Warm up
配置实现步骤:
测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
浏览器: localhost:10004/t2
浏览器访问 http://localhost:10004/t2 快速刷新页面,在前 3 秒,会出现流控异常, 后 3 秒就正常了(如果你刷新非常快 QPS>9 , 仍然会出现流控异常)
注意事项和细节:
排队方式:这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通 过,对应的是漏桶算法。
这种方式主要用于处理间隔性突发的流量,例如消息队列。比如这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的 空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。 类似前面说的 削峰填谷。
匀速排队,阈值必须设置为 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
- 浏览器访问 http://localhost:10004/t2 快速刷新页面 9 次,观察前台/后台输出的情
况
- 输出结果分析
- 没有报错误
- 后台请求排队执行,每隔1s 匀速执行
浏览器访问 http://localhost:10004/t2 快速刷新页面 20 次,当请求等待时间超过 10S, 仍然出现流控异常
线程堆积引出熔断降级
熔断 降级 限流三者的关系:
- 熔断强调的是服务之间的调用能实现自我恢复的状态
- 限流是从系统的流量入口考虑, 从进入的流量上进行限制 达到保护系统的作用。
- 降级, 是从系统业务的维度考虑,流量大了或者频繁异常, 可以牺牲一些非核心业务,保 护核心流程正常使用。
梳理:
- 熔断是降级方式的一种
- 降级又是限流的一种方式
- 三者都是为了通过一定的方式在流量过大或者出现异常时, 保护系统的手段
慢调用比例:
- 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的 慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用
- 当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的 比例大于阈值,则接下来的熔断时长内请求会自动被熔断
- 熔断时长后, 熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响 应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断
- 配置参考:
异常比例:
工作示图:
异常数:
为/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 后)
注意事项和细节:
通过 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 进行修改, 一定确保更新成功.
- 点击 Run sentinel
- 浏览器访问: http://localhost:10004/t4
- 停止 Postman
- 浏览器访问: http://localhost:10004/t4 , 结果正常了(一次返回异常,一次返回正确结果)
注意事项和细节
配置实现步骤
测试
启动 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, 返回正常结果了
注意事项和细节
一个问题引出热点 key 限流
- 热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含 热点参数的资源调用进行限流。
- 热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效
- Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数 级别的流控 https://blog.csdn.net/qq_34416331/article/details/106668747
- 热点参数限流支持集群模式
需求: 通过 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
配置步骤:
浏览器: http://localhost:10004/news?id=1&type=教育
独立设置热点 id=10 的 QPS 阈值(即添加例外)
浏览器: http://localhost:10004/news?id=10&type=教育
注意事项和细节
一个问题引出系统规则
图示解读:
- 系统处理请求的过程想象为一个水管,到来的请求是往这个水管灌水,当系统处理顺 畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT是最短的;
- 反之,当请求堆积的时候,那么处理请求的时间则会变为:排队时间 + 最短处理时间
系统规则:
需求: 通过 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
@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
- 浏览器输入: http://localhost:10004/t6 , 如果 QPS 没有超过 1, 返回正常结果
- 浏览器输入: http://localhost:10004/t6 , 如果 QPS 超过 1, 断路器打开,返回自定义限流处理方法信息。
看一段代码-引出 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 异常/业务异常 。也就是解决前面我们提出的问题解
代码实现:
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 指定方法处理, 其它情况返回正常的结果.
如果希望忽略某个异常,可以使用 exceptionsToIgnore
文档地址: 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
“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”
参与评论
手机查看
返回顶部