• Sentinel服务限流、降级、热点规则


    git地址: https://github.com/alibaba/Sentinel 

    中文文档: https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

       sentinel实际和hystrix的作用一样,实现服务降级、熔断等。但是hystrix的不足之处大概有:1.需要程序员手工搭建监控平台;2.没有一套web界面可以给我们进行细粒度化的配置。Sentinel也是实现流量控制、速率控制、服务熔断、服务降级。Sentinel有的优点如下:1.单独的组件,可以独立出来。2.直接界面化的细粒度统一配置。

    1.下载安装

    1.下载jar包

     到git https://github.com/alibaba/Sentinel/releases  下载即可。例如我下载的是:

    sentinel-dashboard-1.8.0.jar

    2. 启动

    直接以jar包的方式运行即可。

    java -jar ./sentinel-dashboard-1.8.0.jar

    3.访问即可

     默认的端口是8080,启动后访问,登录:  sentinel/sentinel

     2. 新建项目使用sentinel初始化监控

      新建一个项目,注册中心为nacos,端口8848; 熔断流量监控使用sentinel,端口8080.

    1.新建项目cloudalibaba-sentinel-service8401

    2.修改pom

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>cloud</artifactId>
            <groupId>cn.qz.cloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloudalibaba-sentinel-service8401</artifactId>
    
        <dependencies>
            <!--引入自己抽取的工具包-->
            <dependency>
                <groupId>cn.qz.cloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
            <!--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包配置-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </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>
    
    </project>

    3.yml配置

    server:
      port: 8401
    
    spring:
      application:
        name: cloudalibaba-sentinel-service
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 #Nacos服务注册中心地址
        sentinel:
          transport:
            dashboard: localhost:8080 #配置Sentinel dashboard地址
            # 默认就是8719端口,如果占用会依次+1开始扫描
            port: 8719
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'

    4.主启动类

    package cn.qz.cloud.alibaba;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    /**
     * @Author: qlq
     * @Description
     * @Date: 22:06 2020/11/25
     */
    @EnableDiscoveryClient
    @SpringBootApplication
    public class MainApp8401 {
    
        public static void main(String[] args) {
            SpringApplication.run(MainApp8401.class, args);
        }
    }

    5.测试Controller

    package cn.qz.cloud.alibaba.controller;
    
    import cn.qz.cloud.utils.JSONResultUtil;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @Author: qlq
     * @Description
     * @Date: 22:07 2020/11/25
     */
    @RestController
    public class FlowLimitController {
    
        @GetMapping("/testA")
        public JSONResultUtil<String> testA() {
            return JSONResultUtil.successWithData("testA");
        }
    
        @GetMapping("/testB")
        public JSONResultUtil<String> testB() {
            return JSONResultUtil.successWithData("testB");
        }
    }

    6.启动后到nacos查看服务列表

     7. 访问sentinel

    需要注意sentinel采用的是懒加载,如果直接启动服务没有访问,sentinel是不会拦截到请求的。所以需要访问几次服务然后到sentinel中查看。sentinel控制台如下:

    3. sentinel流控

    1.实时监控

     可以实时的查看每个请求的访问QPS情况。

    2.簇点链路

      和上面1差不多。展示最近的访问情况。

    3.流控规则

    新增流控规则的时候,界面如下:

     概念性问题:

    • 资源名:唯一名称,默认请求路径
    • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
    • 阈值类型/单机阈值

    (1)QPS(每秒钟的请求数):当掉该api达到阈值的时候,进行限流

    (2)线程数: 当调用该API的线程数达到阈值时,进行限流

    • 是否集群:不需要集群
    • 流控模式

    (1)直接:直接限流

    (2)关联:当关联的资源达到阈值时,限流自己

    (3)链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】

    • 流控效果

    (1)快速失败:直接失败,抛异常

    (2)Warm up:根据code Factor(冷加载印子,默认3)的值,从阈值/cedeFactor,经过预热时长,才达到预设的QPS

    (3)排队等待: 匀速排队,让请求匀速的通过,阈值类型必须设置QPS。否则无效。

    4. 流控模式使用

    1. QPS 快速失败

    多次访问查看结果如下:

     2. 线程数直接失败

     这个是接收请求,但是当处理的线程数超过1的时候报上面的【Blocked by Sentinel (flow limiting)】。和上面不同的是这个允许请求进去,上面是达到阈值就不让请求进去。

    3.关联流控

      关联流控就是关联的资源达到阈值,限流自己。比如如下:当B超过QPS为1之后,A限流:

     测试方法:

    (1) 使用postman或者jmeter并发访问testB,如下:加到collections中,然后没300ms访问一次,访问30次

    (2)访问A发现A阻塞

     4. 链路流控规则

    (1)查看簇点链路,/testA的资源入口是sentinel_web_servlet_context

     (2) 对A进行链路流控

     (3)频繁访问会报Blocked by Sentinel (flow limiting)

    5. 流控效果

     1.直接快速失败

    默认的就是快速失败,抛出阻塞信息。实现类是 com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

    2.warm up预热: com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController.WarmUpController

    根据code Factor(冷加载印子,默认3)的值,从阈值/cedeFactor,经过预热时长,才达到预设的QPS。比如下面规则表示:初始的时候阈值是10/3 = 3 ;5s的预热时间达到10。

     这种模式的应用场景是秒杀系统,开启瞬间会有很多流量上来,很有可能把系统打死,预热就是为了保护系统,让流量缓慢的进来。

    3. 排队等待

    匀速排队,让请求匀速的通过。这种很好理解,就是每秒钟放行多少个请求。如下:每秒钟处理1个,超过的话进行排队;排队时长超过2s报阻塞错误。源码是com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController。采用的算法是令牌桶算法。

     4.sentinel熔断降级

    参考: https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

    提供以下几种熔断策略:

    (1)慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
    (2)异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
    (3)异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

    1. 慢调用比例模式

    满足两个条件会触发熔断:

    1》单位统计时长请求数大于设置的最小值。下面是每秒钟5个。

    2》慢请求达到设置的比例。

     (1)新增方法

        @GetMapping("/testD")
        public JSONResultUtil<String> testD() throws InterruptedException {
            TimeUnit.SECONDS.sleep(1);
            return JSONResultUtil.successWithData("testD");
        }

    (2)jmeter 以每秒钟10次请求访问

    (3)curl访问

    $ curl http://localhost:8401/testD
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100    35    0    35    0     0    249      0 --:--:-- --:--:-- --:--:--   280Blocked by Sentinel (flow limiting)

    (4)上面jmeter每秒钟10次请求大于最小值5;每个请求处理时长超过200ms,满足第二个条件,因此触发降级。

     2. 异常比例模式

    如下:这个也是需要两个条件。

    1》每秒钟请求超过五个

    2》异常比例超过50%触发熔断

     (1)新增方法

        @GetMapping("/testE")
        public JSONResultUtil<String> testE() {
            int i = 1 / 0;
            return JSONResultUtil.successWithData("testE");
        }

    (2)jmeter每秒钟十个访问

    (3)curl访问:发现触发降级

    $ curl http://localhost:8401/testE
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100    35    0    35    0     0    140      0 --:--:-- --:--:-- --:--:--   149Blocked by Sentinel (flow limiting)

     (4)停掉jmeter再次curl,直接爆程序错误。也就是没触发sentinel熔断

    $ curl http://localhost:8401/testE
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
      0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0{"timestamp":"2020-12-01T14:41:15.196+0000","status":500,"error":"Internal Server Error","message":"/ by zero","trace":"java.lang.ArithmeticException: / by zero
    	at cn.qz.cloud.alibaba.controller.FlowLimitController.testE(FlowLimitController.java:43)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
    	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
    	at org.springframework.web.servl

    3.异常数模式

    和上面的区别是根据异常数判断是否需要进行熔断。

     5. 热点规则

    参考:https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81

      这个和hystrix挺像的。@SentinelResource代替@HystrixCommand。

    1.新增方法

        @GetMapping("/testHotKey")
        @SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
        public JSONResultUtil<String> testHotKey(@RequestParam(value = "p1", required = false) String p1,
                                                 @RequestParam(value = "p2", required = false) String p2) {
            return JSONResultUtil.successWithData("testHotKey");
        }
    
        public JSONResultUtil<String> deal_testHotKey(String p1, String p2, BlockException exception) {
            return JSONResultUtil.successWithData("deal_testHotKey");  //sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
        }

    解释:

    @SentinelResource注解的value是便于唯一标识,在后面sentinel限流配置的时候可以使用 /testHotKey ,也可以使用 testHotKey唯一标识

    blockHandler  类似于hystrixCommand注解的兜底方法。

    2.sentinel增加如下配置:

     上面的配置规则是:

    (1)如果 testHotKey 方法不带p1 参数不进行限流

    (2)如果待了p1参数且值不为"1",阈值为2;如果值为"1", 阈值可达到20。 (这里需要注意例外项的参数类型只支持8种基本数据类型和String类型)

    (3)p2参数不参数限流

     6. 系统规则

    参考:https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81

      系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

    • 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 达到阈值即触发系统保护。

    可以理解为系统规则是全局的限流配置,可以针对服务的全局QPS、机器的CPU等参数进行限流。

    7.@SentinelResource

      这个注解类似于HystrixCommand注解,可以用于方法的降级处理。

    1.常规用法

    package cn.qz.cloud.alibaba.controller;
    
    import cn.qz.cloud.utils.JSONResultUtil;
    import com.alibaba.csp.sentinel.annotation.SentinelResource;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class RateLimitController {
    
        @GetMapping("/byResource")
        @SentinelResource(value = "byResource", blockHandler = "handleException")
        public JSONResultUtil<String> byResource() {
            return JSONResultUtil.successWithData("byResource");
        }
    
        public JSONResultUtil<String> handleException(BlockException exception) {
            return JSONResultUtil.successWithData("handleException");
        }
    
        @GetMapping("/rateLimit/byUrl")
        @SentinelResource(value = "byUrl")
        public JSONResultUtil<String> byUrl() {
            return JSONResultUtil.successWithData("byUrl");
        }
    }

    (1)新建两条限流规则:

    第一条: 资源名是@SentinelResource 的value属性

     第二条: 资源名是URL,等价于上面的@SentinelResource的value属性

     (2)测试:

    1》 byResource 超出会走自己指定的方法

     2》byUrl 超出后走默认的限流方法

     (3)存在的问题:

    1》系统默认限流,满足不了业务要求。

    2》自定义的处理方法和业务代码耦合在一起,不直观。

    3》每个方法都添加一个兜底的,代码膨胀加剧。

    4》全局统一的处理方法没有体现。

     2. 自定义异常处理器

    (1)新建异常处理器

    package cn.qz.cloud.alibaba.myhandler;
    
    import cn.qz.cloud.utils.JSONResultUtil;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    
    public class CustomerBlockHandler {
    
        public static JSONResultUtil<Object> handlerException(BlockException exception) {
            return JSONResultUtil.errorWithMsg("-1", "error");
        }
    
        public static JSONResultUtil<Object> handlerException2(BlockException exception) {
            return JSONResultUtil.errorWithMsg("-1", "error2");
        }
    }

    (2)Controller使用@SentinelResource注解指明异常处理器以及方法

        @GetMapping("/rateLimit/cusError")
        @SentinelResource(value = "cusError", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
        public JSONResultUtil<String> cusError() {
            return JSONResultUtil.successWithData("cusError");
        }

    (3)新建流控

     (3) 测试超出限制

    8.@SentnelResource 补充

    主要属性 fallback和blockHandler。fallback管运行异常,blockHandler管配置违规,包括流量超限等。IllegalArgumentException 可以指定忽略的异常。例如:

    package cn.qz.cloud.alibaba.controller;
    
    import cn.qz.cloud.bean.Payment;
    import cn.qz.cloud.utils.JSONResultUtil;
    import com.alibaba.csp.sentinel.annotation.SentinelResource;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import javax.annotation.Resource;
    
    @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 JSONResultUtil<Payment> fallback(@PathVariable Long id) {
            JSONResultUtil<Payment> result = restTemplate.getForObject(SERVICE_URL + "/list/" + id, JSONResultUtil.class, id);
            if (id == 4) {
                throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
            } else if (result.getData() == null) {
                throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
            }
    
            return result;
        }
    
        //本例是fallback
        public JSONResultUtil<Payment> handlerFallback(@PathVariable Long id, Throwable e) {
            JSONResultUtil<Payment> objectJSONResultUtil = JSONResultUtil.successWithData(null);
            objectJSONResultUtil.setMsg("handlerFallback: " + e);
            return objectJSONResultUtil;
        }
    
        //本例是blockHandler
        public JSONResultUtil<Payment> blockHandler(@PathVariable Long id, BlockException blockException) {
            JSONResultUtil<Payment> objectJSONResultUtil = JSONResultUtil.successWithData(null);
            objectJSONResultUtil.setMsg("blockHandler : " + blockException);
            return objectJSONResultUtil;
        }
    }

    nacos-payment-provider服务只有1、2、3 ID返回数据。测试:

    (1)ID为4抛出非法参数异常,这种异常exceptionsToIgnore 忽略兜底

     (2)ID为4以后的走fallback

    9.sentinel持久化 

      持久化需要借助nacos,就相当于配置信息存到nacos,然后后nacos读取配置。

    1.应用的yml增加如下配置:

    spring:
      application:
        name: nacos-order-consumer
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
        sentinel:
          transport:
            #配置Sentinel dashboard地址
            dashboard: localhost:8080
            #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
            port: 8719
            datasource:
              ds1:
                nacos:
                  server-addr: localhost:8848
                  dataId: nacos-order-consumer
                  groupId: DEFAULT_GROUP
                  data-type: json
                  rule-type: flow

    2.到nacos新建配置

    这里需要注意,data Id 和 上面应用中配置的一样。

     内容如下:

    [
        {
            "resource": "/consumer/fallback/2",
            "limitApp": "default",
            "grade": 1,
            "count": 5,
            "strategy": 0,
            "controlBehavior": 0,
            "clusterMode": false
        }
    ]

    解释:

    resource:资源名称
    limitApp:来源应用
    grade:阈值类型,0表示线程数,1表示QPS
    count:单击阈值
    strategy:流控模式,0表示直接,1表示关联,2表示链路
    controlBehavior:流控效果,0表示快速失败,1表示Warm up,2表示排队等待

  • 相关阅读:
    关于C语言中类型的理解,有符号无符号的理解以及浮点数的理解
    关于集中注意力,情绪管理,记忆的总结整体
    关于链表逆置的问题
    git中reset和checkout的总结整理
    git中关于分支和stash的理解
    SVN和git的区别
    shell命令之find的用法
    (转载)获取服务器响应时间
    (转载)Spring定时任务的几种实现
    (转载)spring单例和多例详解。如何在单例中调用多例对象
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/13909052.html
Copyright © 2020-2023  润新知