编辑
哈喽~大家好,SpringCloud Sentinel 使用。
🥇个人主页:个人主页
🥈 系列专栏:【微服务】
🥉推荐专栏: JavaEE框架
目录
一、前言
1、什么是Sentinel?
Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。 官网:https://github.com/alibaba/Sentinel/wiki
Sentinel 具有以下特征:
丰富的应用场景 :Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即 突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控 :Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机 器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态 :Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快 速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel的生态圈
编辑
Sentinel 主要特性
编辑
2、安装与启动
打开网址:https://github.com/alibaba/Sentinel/releases
找到合适的版本进行下载
编辑
下载本地后保存到任意目录(不要中文目录)然后在该目录下打开cmd使用命令打开
windows版本
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.0.jar
linux版本
java -jar sentinel-dashboard-1.8.1.jar
编辑
有上图的界面就是已经启动成功了,在浏览器输入 http://localhost:8080/ (注意端口号占用问题,默认的8080与tomcat默认的端口号相同)
账号与密码都是 sentinel
编辑
看到这界面就启动成功了
二、SpringBoot整合Sentinel
1、说明
在以后我们的项目部署上线的时候,会有各种各样的服务突发问题,比如雪崩效应(在分布式系统中,由于网络原因或自身的原因,服务一般无法保证 100% 可用。如果一个服务出现了问题,调用这个服务就会出现线程阻塞的情况,此时若有大量的请求涌入,就会出现多条线程阻塞等待,进而导致服务瘫痪。由于服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的雪崩效应)等问题。
sentinel组件由2部分构成:核心库与控制台
核心库(Java 客户端)不依赖任何框架/库,能够运行于所有Java 运行时环境,同时对Dubbo / Spring Cloud等框架也有较好的支持。
控制台(Dashboard) 基于Spring Boot开发,打包后可以直接运行,不需要额外的Tomcat等应用容器。
2、将服务注册到Sentinel
pom文件要导入的依赖
<dependencies> <!--SpringCloud ailibaba nacos --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <!--SpringCloud ailibaba sentinel --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- SpringBoot整合Web组件+actuator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--日常通用jar包配置--> <!-- Spring Boot 提供了一组开发工具 spring-boot-devtools 可以提高开发者的工作效率,开发者可以将该模块包含在任何项目中,spring-boot-devtools 最方便的地方莫过于热部署了。--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>4.6.3</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
.yaml文件
server: port: 8401 spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: #Nacos服务注册中心地址 server-addr: localhost:8848 sentinel: transport: #配置Sentinel dashboard地址 dashboard: localhost:8080 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口 port: 8719 management: endpoints: web: exposure: include: '*'
主启动类
@EnableDiscoveryClient @SpringBootApplication public class MainApp8401 { public static void main(String[] args) { SpringApplication.run(MainApp8401.class, args); } }
业务类FlowLimitController
@RestController public class FlowLimitController { @GetMapping("/testA") public String testA() { return "------testA"; } @GetMapping("/testB") public String testB() { return "------testB"; } }
启动我们的项目,访问路径 http://localhost:8401/testA,效果如下
编辑
此时服务已经注册进来了
编辑
三、功能展示
1、实时监控
同一个服务下的所有机器的簇点信息会被汇总,并且秒级地展示在"实时监控"下。
编辑
注意: 实时监控仅存储 5 分钟以内的数据,如果需要持久化,需要通过调用实时监控接口来定制。
注意:请确保 Sentinel 控制台所在的机器时间与自己应用的机器时间保持一致,否则会导致拉不到实时的监控数据。
2、流量控制
什么是QPS?
流量控制,其原理是监控应用流量的QPS(每秒查询率) 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
流量控制在网络传输中是一个常用的概念,它用于调整网络包的数据。任意时间到来的请求往往是 随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。
QPS (每秒钟的请求数量) :当调用该api的QPS达到阈值的时候,进行限流 (点击超过某数量的时候作出限制) 线程数:当调用该api的线程数达到阈值的时候,进行限流 (多个线程进行手写)
配置流控模式
sentinel共有三种流控模式,分别是:
直接(默认):接口达到限流条件时,开启限流
关联:当关联的资源达到限流条件时,开启限流 [适合做应用让步]
链路:当从某个接口过来的资源达到限流条件时,开启限流下面呢分别演示三种模式:
直接流控模式
直接流控模式是最简单的模式,当指定的接口达到限流条件时开启限流。上面案例使用的就是直接流控 模式。
关联流控模式
关联流控模式指的是,当指定接口关联的接口达到限流条件时,开启对指定接口开启限流。 (当与A关联的资源B达到阀值后,就限流A自己,简称:B惹事,A挂了)
链路流控模式
链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流。它的功能有点类似于针对配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度更细。
3、配置流控效果
快速失败(默认): 直接失败,抛出异常,不做任何额外的处理,是最简单的效果
Warm Up:它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的 (QPS阈值开始很低,过一小会访问很高,就会有一个缓冲的有个阶段,一个慢热的过程)
1/3,然后慢慢增长,直到最大阈值,适用于将突然增大的流量转换为缓步增长的场景。
排队等待:让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的排队等待; 它还会让设置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。
具体可查看下图
编辑
4、降级规则
降级规则就是设置当满足什么条件的时候,对服务进行降级。Sentinel提供了三个衡量条件:
平均响应时间(RT) :当资源的平均响应时间超过阈值(以 ms 为单位)之后,资源进入准降级状态。如果接下来 1s 内持续进入 5 个请求,它们的 RT都持续超过这个阈值,那么在接下的时间窗口 (以 s 为单位)之内,就会对这个方法进行服务降级。
编辑
咋在200ms内搞定,搞不定就1秒中内关闭匝道
异常比例:当资源的每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的 时间窗口(以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0]。
编辑
在异常比例超过0.2,未来的1秒内不可用
异常数 :当资源近 1 分钟的异常数目超过阈值之后会进行服务降级。注意由于统计时间窗口是分钟级别的,若时间窗口小于 60s,则结束熔断状态后仍可能再进入熔断状态。
编辑
在异常数超过5个时,未来的70秒内不可用
5、热点规则
热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上。
简单来说就是,参数只要QPS超过每秒n次,马上降级处理
eg:
@GetMapping("/testHotKey") @SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey") public String testHotKey(@RequestParam(value = "p1", required = false) String p1, @RequestParam(value = "p2", required = false) String p2) { //int age = 10/0; return "------testHotKey"; } public String deal_testHotKey(String p1, String p2, BlockException exception) { return "------deal_testHotKey,o(╥﹏╥)o"; //sentinel系统默认的提示:Blocked by Sentinel (flow limiting) }
访问路径是testHotKey时,如果参数只要QPS超过每秒n次,马上降级处理,调用deal_testHotKey方法。(超过一秒一下就寄了,参数索引就是参数的下标)
编辑
将p1去掉,剩下p2,快速访问,http://localhost:8401/testHotKey?p2=1,发现没有作出限制,这是就要配置参数例外项了
编辑
当我们p1=5时,就会有问题,http://localhost:8401/testHotKey?p1=5
6、系统规则
系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 、CPU 使用率和线程数五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量 (进入应用的流量) 生效。
Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般 是 CPU cores * 2.5。
RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
CPU使用率:当单台机器上所有入口流量的 CPU使用率达到阈值即触发系统保护。
(我们有很多的服务,将这些服务包装起来,形成了一个服务群,而系统规则就是在将服务群包装起来,它的配置对全部的服务都生效,一般不建议使用这个,做一些细致化的服务配置很不友好)
@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:
value:资源名称,必需项(不能为空) entryType:entry 类型,可选项(默认为EntryType.OUT)
用于定义资源,并提供可选的异常处理和 fallback 配置项。其主要参数如下:
属性 | 作用 |
value | 资源名称 |
entryType | entry类型,标记流量的方向,取值IN/OUT,默认是OUT |
blockHandler | 处理BlockException的函数名称,函数要求: 1. 必须是 public 2.返回类型 参数与原方法一致 3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法。 |
blockHandlerClass | 存放blockHandler的类,对应的处理函数必须static修饰。 |
fallback | 用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求: 1. 返回类型与原方法一致 2. 参数类型需要和原方法相匹配 3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定fallbackClass里面的方法。 |
fallbackClass | 存放fallback的类。对应的处理函数必须static修饰。 |
defaultFallback | 用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求: 1. 返回类型与原方法一致 2. 方法参数列表为空,或者有一个 Throwable 类型的参数。 3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定 fallbackClass 里面的方法。 |
exceptionsToIgnore | 指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入 fallback逻辑,而是原样抛出。 |
exceptionsToTrace | 需要trace的异常 |
在实际开发当中经常出现,服务限流,但是超出规则的默认返回提示,这样不太符合业务逻辑。
所以就有了兜底方法。
注:千万注意,兜底的方法的参数,和原来方法的参数必须一致,并且兜底的方法里面需要增加一个【BlockException exception】参数。定义全局兜底的类,里面写如全局兜底的方法,注意,返回类型,参数,一致,且都需要添加异常类,并且方法的修饰符必须是public
6.1、代码
@GetMapping("/byResource") @SentinelResource(value = "byResource", blockHandler = "handleException") public CommonResult byResource() { return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, "serial001")); } public CommonResult handleException(BlockException exception) { return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用"); }
6.2、效果
编辑
sentinel默认的错误信息是:Blocked by Sentinel (flow limiting);如何自定义一个错误信息呢?
6.3、代码
@GetMapping("/testHotKey") @SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey") public String testHotKey(@RequestParam(value = "p1", required = false) String p1, @RequestParam(value = "p2", required = false) String p2) { // int age = 10/0; return "------testHotKey"; } public String deal_testHotKey(String p1, String p2, BlockException exception) { return "------deal_testHotKey,o(╥﹏╥)o"; //sentinel系统默认的提示:Blocked by Sentinel (flow limiting) }
6.4、添加流控规则
编辑
6.5、触发效果
编辑
我们将// int age = 10/0; 取消注释跑一下
编辑
报错了
7、熔断
这很明显黑道我们自定义的流控规则,而是直接熔断了,啥是熔断?
熔断是一种系统保护机制。美股熔断是对股市极端情况下的一种保护。举个生活中的例子,比如保险器,当电流过大时保险器里面的保险丝就会断掉以此保护家里的电器不会被损坏。
同理,在应用系统中也有熔断器,系统在运行时遇到极端情况,熔断器便会截断请求来保护系统,不至于让系统崩溃。
直接贴代码
@RestController @Slf4j public class CircleBreakerController { public static final String SERVICE_URL = "http://nacos-payment-provider"; @Resource private RestTemplate restTemplate; @RequestMapping("/consumer/fallback/{id}") // @SentinelResource(value = "fallback") //没有配置 // @SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常 // @SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规 @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler", exceptionsToIgnore = {IllegalArgumentException.class}) public CommonResult<Payment> fallback(@PathVariable Long id) { CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id); if (id == 4) { throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常...."); }else if (result.getData() == null) { throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常"); } return result; } //本例是fallback public CommonResult handlerFallback(@PathVariable Long id,Throwable e) { Payment payment = new Payment(id,"null"); return new CommonResult<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment); } //本例是blockHandler public CommonResult blockHandler(@PathVariable Long id,BlockException blockException) { Payment payment = new Payment(id,"null"); return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment); } //==================OpenFeign @Resource private PaymentService paymentService; @GetMapping(value = "/consumer/paymentSQL/{id}") public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) { return paymentService.paymentSQL(id); } }
id为1看看
编辑
我们这里id只有1,2,3数据,其他的都会报错,例如这样的错
编辑
用户使用时,肯定不会显示这样的错误信息,但我们配置之后,哪怕出错了,都可以更好的解决
编辑
注:fallback只负责业务异常,blockHandler只负责sentinel控制台配置违规
四、规则持久化
1、规则丢失
无论是通过硬编码的方式来更新规则,还是通过接入 Sentinel Dashboard 后,在页面上操作来更新规则,都无法避免一个问题,那就是服务重新后,规则就丢失了,因为默认情况下规则是保存在内存中的。
简单来说就是服务重启了,那些规则没有了。
2、如何做?
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台 的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效
编辑
然后添加流控规则
编辑
重启一下,规则还在,我们的持久化成功了。
不积跬步无以至千里,趁年轻,使劲拼,给未来的自己一个交代!向着明天更好的自己前进吧!
编辑