Sentinel限流熔断

当一个网站被大量用户访问时,会引起服务器过载,导致系统崩溃,因此需要对网站流量来进行管控,防止在同一时间因为大量用户访问而导致系统崩溃。

Sentienl概述

Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。
Sentinel核心分为两个部分:

  • 核心库(Java 客户端):能够运行于所有 Java 运行时环境,同时对Dubbo /Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard):基于 Spring Boot 开发,打包后可以直接运行。

    安装

    下载地址:https://github.com/alibaba/Sentinel/releases

    启动

    在存放Sentinel Jar包的目录启动命令行输入命令启动
    1. java -Dserver.port=8180 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar
    以上命令可以启动Sentinel,并设置端口号为8180

    访问

    访问http://localhost:8180进入Sentinel登陆页面,账号密码都为sentinel。如下所示:
    image.png

    Spring Boot集成Sentinel

  1. 在服务消费方添加依赖 ```xml com.alibaba.cloud spring-cloud-starter-alibaba-sentinel

org.springframework.boot spring-boot-starter-actuator

  1. 2. 修改配置文件
  2. ```yaml
  3. server:
  4. port: 9010
  5. spring:
  6. application:
  7. name: consumer
  8. cloud:
  9. nacos:
  10. server-addr: localhost:8848
  11. sentinel:
  12. transport:
  13. port: 8099 #用于与Sentinel控制台交互的端口,指定未使用的端口即可
  14. dashboard: localhost:8180 #指定sentinel控制台地址
  15. feign:
  16. hystrix:
  17. enabled: true
  1. 启动服务消费方,并在浏览器中调用服务消费方,将会在Sentinel控制台中显示配置文件中配置Sentinel服务的服务名

image.png

  1. 对服务节点进行限流

需要在控制台中选择需要限流的服务的簇点链路,选择需要限流的url,点击流控进行限流策略设置,下图流控规则中阈值类型的QPS代表每秒请求次数,单机阈值,代表一个ip每秒可以请求的次数,设置单机与之后反复刷新设置限流规则的url将会有限流信息提示。
image.png

Sentinel流控规则

阈值类型:

  • QPS(Queries Per Second):当调用相关url对应的资源时,QPS达到单机阈值时,就会限流。
  • 线程数:当调用相关url对应的资源时,线程数达到单机阈值时,就会限流。

限流模式
Sentinel的限流模式由三个分别为直接模式、关联模式、链路模式

直接模式

sentinel默认的模式即为直接模式+快速失败,如上实例所示。

关联模式

关联模式是当关联的资源达到阈值,就将自己限流。常用于当两个接口抢占资源时,当其中一个接口达到访问阈值就将与其关联的另一个接口限流,保证资源倾向于被频繁访问的接口。
示例:
在关联资源中设置要关联的url。
image.png

链路模式

链路模式只记录指定链路入口的流量。也就是当多个服务对指定资源调用时,假如流量超出了指定阈值,则进行限流。被调用的方法用@SentinelResource进行注解,然后分别用不同业务方法对此业务进行调用。但当A业务被限流后但不会影响B业务的执行。
示例:

  1. 创建限流方法

    1. @Service
    2. public class SentinelConsumerService {
    3. @SentinelResource("doSomeThing")
    4. public String getResource() {
    5. return "Resource";
    6. }
    7. }
  2. 在Controller中注入刚刚创建的Service,并在需要进行限流的链路url中调用该方法 ```java @RestController @RequestMapping(“/consumer”) public class TestController { @Value(“${spring.application.name}”) private String applicationName;

    @Autowired private RestTemplate loadBalancedRestTemplate;

    @Autowired private SentinelConsumerService sentinelConsumerService;

    @GetMapping(“/annotationGetName”) public String annotationLoadBalanceTransfer() {

    1. sentinelConsumerService.getResource();
    2. String url = StrUtil.format("http://provider/provider/name/{}", applicationName);
    3. return loadBalancedRestTemplate.getForObject(url, String.class);

    }

}

  1. 在新出现的链路中添加流控规则<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21402661/1632149989896-ac1a9473-0fa9-4c27-854a-831eaf82ec2c.png#clientId=u17cd5837-4e86-4&from=paste&height=420&id=u38156d6a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=840&originWidth=1643&originalType=binary&ratio=1&size=67873&status=done&style=none&taskId=uc04f9e23-bb7d-4adc-837b-ab1b16dda24&width=821.5)<br />在流控规则中添加入口资源名称。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21402661/1632150070145-c8fc3dc7-fc3b-46fb-a11d-8a04b8cdd852.png#clientId=u17cd5837-4e86-4&from=paste&height=347&id=uc3d95e0e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=694&originWidth=851&originalType=binary&ratio=1&size=54091&status=done&style=none&taskId=u5d51618f-add1-4055-bea7-be20c06c9ef&width=425.5)<br />流控模式为链路模式时,假如是sentinel 1.7.2以后版本,Sentinel Web过滤器默认会聚合所有URL的入口为sentinel_spring_web_context,因此单独对指定链路限流会不生效,需要在application.yml添加如下语句来关闭URL PATH聚合.
  2. ```java
  3. server:
  4. port: 9010
  5. spring:
  6. application:
  7. name: consumer
  8. cloud:
  9. nacos:
  10. server-addr: localhost:8848
  11. sentinel:
  12. transport:
  13. port: 8099
  14. dashboard: localhost:8180
  15. web-context-unify: false #关闭URL PATH聚合
  16. feign:
  17. hystrix:
  18. enabled: true

限流效果

Sentinel限流效果有三种:快速失败、WarmUp(预热)、排队等待

快速失败

Sentinel默认的流控效果未快速失败;

预热

WarmUp也叫预热,根据codeFactor(默认3)的值,(阀值/codeFactor)为初始阈值,经过预热时长,才到达设置的QPS的阈值,假如单机阈值为100,系统初始化的阈为 100/3 ,即阈值为33,然后过了10秒,阈值才恢复到100。这个预热的应用场景,如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。

排队等待

从字面上面就能够猜到,匀速排队,让请求以均匀的速度通过,阈值类型必须设成QPS,否则无效。比如有时候系统在某一个时刻会出现大流量,之后流量就恢复稳定,可以采用这种排队模式,大流量来时可以让流量请求先排队,等恢复了在慢慢进行处理

熔断与降级

概述

熔断

熔断是指当下游服务器因为某种原因突然变得不可用响应过慢,为了防止出现服务雪崩,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。

服务雪崩

服务雪崩指当存在类似如下服务调用时,即由上游服务调用下游服务时,若由于服务1访问量突然剧增,即使服务1承受住了突增的请求,但服务2或服务3也有可能宕机,当服务3崩溃后会导致服务2和服务1慢慢堵塞,这样由于下游服务的崩溃而导致整个服务链路崩溃的现象称之为服务雪崩.
Sentinel - 图5

降级

服务降级是从整个系统的负荷情况出发和考虑的,对某些负荷会比较高的情况,为了预防某些功能(业务场景)出现负荷过载或者响应慢的情况,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的fallback(退路)错误处理信息。这样,虽然提供的是一个有损的服务,但却保证了整个系统的稳定性和可用性。

示例

模拟慢调用下的降级,修改Controller

  1. @Autowired
  2. private RestTemplate restTemplate;
  3. @Value("${spring.application.name}")
  4. private String appName;
  5. private AtomicLong atomicLong = new AtomicLong(1);
  6. @GetMapping("/doRestEcho0")
  7. public String doRestEcho1() throws InterruptedException {
  8. long num = atomicLong.getAndIncrement();
  9. if (num % 2 == 0) {
  10. Thread.sleep(200);
  11. }
  12. //1.定义要用的API
  13. String url = "http://localhost:8081/provider/echo/" + appName;
  14. //2.定义访问API的方法
  15. return restTemplate.getForObject(url, String.class);
  16. }

其中AtomicLong为线程安全的自增与自减,其参数为初始值,方法getAndIncrement()相当于a++;下面的if语句将会当num为偶数时使服务休眠200毫秒.

  1. 设置限流规则

在指定路由上设置限流规则,当发生请求异常的比例达到0.5时将会进行服务熔断
image.png

  1. 自定义异常处理 ```java @Component public class ServiceBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
    1. //1.设置响应数据类型
    2. response.setContentType("application/json,charset=uft-8");
    3. //2.设置响应业务数据
    4. PrintWriter writer = response.getWriter();
    5. Map<String, Object> map = new HashMap<>();
    6. map.put("status", 429);
    7. response.setStatus((Integer) map.get("status"));
    8. if (e instanceof DegradeException) {
    9. map.put("msg", "请求过于频繁,请稍后再试");
    10. } else {
    11. map.put("msg", "服务暂时不可用");
    12. }
    13. //将map集合转为JSON
    14. String jsonStr = new ObjectMapper().writeValueAsString(map);
    15. writer.println(jsonStr);
    16. writer.flush();
    17. writer.close();
    } }
  1. <a name="Mxejy"></a>
  2. ## Sentinel降级策略
  3. Sentinel 提供以下几种熔断策略:
  4. - 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  5. - 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  6. - 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
  7. **注意:**异常降级**仅针对业务异常**,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常。
  8. 熔断降级规则(DegradeRule)包含下面几个重要的属性:
  9. | **Field** | **说明** | **默认值** |
  10. | --- | --- | --- |
  11. | resource | 资源名,即规则的作用对象 | |
  12. | grade | 熔断策略,支持慢调用比例/异常比例/异常数策略 | 慢调用比例 |
  13. | count | 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 | |
  14. | timeWindow | 熔断时长,单位为 s | |
  15. | minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | 5 |
  16. | statIntervalMs | 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) | 1000 ms |
  17. | slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) | |
  18. <a name="i09UR"></a>
  19. ### 慢调用比例
  20. 慢调用指耗时大于阈值RT(Response Time)的请求称为慢调用,阈值RT由用户设置。其属性具体含义说明如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21402661/1632535386505-2870724f-6a33-48e8-9053-7e2c6178c079.png#clientId=u88429d78-ade2-4&from=paste&id=ua37b6f66&margin=%5Bobject%20Object%5D&name=image.png&originHeight=367&originWidth=1054&originalType=url&ratio=1&size=118396&status=done&style=none&taskId=ubdc78885-7b2a-40b3-a32b-b0c571a2aee)<br />慢调用逻辑中的状态分析如下:
  21. - 熔断(OPEN):请求数大于最小请求数并且慢调用的比率大于比例阈值则发生熔断,熔断时长为用户自定义设置。
  22. - 探测(HALFOPEN):当熔断过了定义的熔断时长,状态由熔断(OPEN)变为探测(HALFOPEN)。
  23. - 关闭(CLOSED):如果接下来的一个请求小于最大RT,说明慢调用已经恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED),如果接下来的一个请求大于最大RT,说明慢调用未恢复,继续熔断,熔断时长保持一致
  24. 注意:Sentinel默认统计的RT上限是4900ms,超出此阈值的都会算作4900ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx来配置
  25. <a name="eJR4u"></a>
  26. ### 异常比例
  27. 当资源的每秒请求数大于等于最小请求数,并且异常总数占通过量的比例超过比例阈值时,资源进入降级状态。其属性说明如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21402661/1632535425933-59a284f5-6936-4ec7-ac8a-8bc1d099840c.png#clientId=u88429d78-ade2-4&from=paste&id=uad722740&margin=%5Bobject%20Object%5D&name=image.png&originHeight=288&originWidth=1050&originalType=url&ratio=1&size=97150&status=done&style=none&taskId=udce07449-0ce2-4b9e-8d8f-3272c2fb93c)
  28. 异常比例中的状态分析如下:
  29. - 熔断(OPEN):当请求数大于最小请求并且异常比例大于设置的阈值时触发熔断,熔断时长由用户设置。
  30. - 探测(HALFOPEN):当超过熔断时长时,由熔断(OPEN)转为探测(HALFOPEN)
  31. - 关闭(CLOSED):如果接下来的一个请求未发生错误,说明应用恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED)。如果接下来的一个请求继续发生错误,说明应用未恢复,继续熔断,熔断时长保持一致。
  32. <a name="x4AOY"></a>
  33. ### 异常数量
  34. 当资源近1分钟的异常数目超过阈值(异常数)之后会进行服务降级。注意,由于统计时间窗口是分钟级别的,若熔断时长小于60s,则结束熔断状态后仍可能再次进入熔断状态。其属性说明如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21402661/1632535476746-797575be-d9dc-4726-b18e-f97a22386d38.png#clientId=u88429d78-ade2-4&from=paste&id=u1e9972d4&margin=%5Bobject%20Object%5D&name=image.png&originHeight=296&originWidth=1053&originalType=url&ratio=1&size=89462&status=done&style=none&taskId=ud9bc6f62-aa64-471c-82be-8991573336a)<br />基于异常数的状态分析如下:
  35. - 熔断(OPEN):当请求数大于最小请求并且异常数量大于设置的阈值时触发熔断,熔断时长由用户设置。
  36. - 探测(HALFOPEN):当超过熔断时长时,由熔断(OPEN)转为探测(HALFOPEN)
  37. - 关闭(CLOSED):如果接下来的一个请求未发生错误,说明应用恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED)如果接下来的一个请求继续发生错误,说明应用未恢复,继续熔断,熔断时长保持一致。
  38. <a name="NDrZh"></a>
  39. ## 热点规则
  40. <a name="Cc1Fz"></a>
  41. ### 概述
  42. 热点即为经常访问的数据.因此热点规则可以根据某个经常访问的数据进行限流,不同于普通限流规则的是,热点规则只会针对携带热点参数的请求进行限流
  43. <a name="hO0IA"></a>
  44. ### 示例:
  45. 1. 创建Controller
  46. ```java
  47. @GetMapping("/findById")
  48. @SentinelResource("res")
  49. public String doFindId(Integer id) {
  50. return "id为:" + id;
  51. }

@SentinelResource注解可以根据value值来找到指定的资源规则即根据上面的res来找到对应的热点规则如下所示:
image.png
设置热点规则:
image.png
参数索引为要设置热点规则的参数下标,此下标与数组下标相同由0开始.例如0代表第一个参数,1代表第二个参数.
单机阈值以及统计窗口时长表示在此窗口时间超过阈值就限流。

针对特定参数
image.png
高级选项中可以设置例外参数,表示这个参数只有达到该阈值时才会限流,而不是应用普通规则

系统规则

Sentinel的系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load(负载)、RT(响应时间)、入口 QPS 、线程数和CPU使用率五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统规则是一种全局设计规则,其中,

  • Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps minRt 计算得出。设定参考值一般是 CPU cores 2.5。
  • CPU使用率:当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)。
  • RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

注意:系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务。

授权规则

Sentinel中存在黑白名单规则,黑白名单根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

示例1:

  1. 创建RequestOriginParser接口的实现类,基于业务在接口方法中解析请求数据并返回,底层会基于此返回值进行授权规则应用。RequestOriginParser接口功能为从 HTTP 请求中解析请求源(例如 IP、用户、应用程序名称),其只有一个方法为parseOrigin作用为从给定的 HTTP 请求解析来源,参数为HTTP请求Request

    1. @Component
    2. public class DefaultRequestOriginParser implements RequestOriginParser {
    3. @Override
    4. public String parseOrigin(HttpServletRequest request) {
    5. //getParameter的参数用于指定参数名称并将其返回
    6. return request.getParameter("origin");
    7. }
    8. }
  2. 流控规则定义

image.png
添加此白名单后,携带origin参数的请求只有名为mazhao和zhaobiao的参数可以正常访问资源,其他资源将会被拦截掉.

示例2:根据ip地址设置黑白名单

  1. 修改RequestOriginParser接口的实现类,使其可进行ip拦截

    1. @Component
    2. public class DefaultRequestOriginParser implements RequestOriginParser {
    3. @Override
    4. public String parseOrigin(HttpServletRequest request) {
    5. request.getParameter("origin");
    6. //根据ip地址进行黑白名单设置
    7. return request.getRemoteAddr();
    8. }
    9. }
  2. 设置流控规则

image.png
设置IP地址后,可以发现除了172.18.5.127这个ip会被拦截,其他的ip地址的请求不会被拦截