• Sentinel: 分布式系统的流量防卫兵



    官方文档

    1 Sentinel简介

    官方文档

    Sentinel 介绍

    Sentinel 对比 Hystrix(选型与简介)

    Sentinel v1.7 文档手册

    1.1官方介绍

    Sentinel 是什么?
    随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。

    Sentinel 具有以下特征:

    • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
    • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500
      台以下规模的集群的汇总运行情况。
    • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
    • 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI
      扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等

    1.2 总结

    Sentinel 是分布式系统的防御系统。以流量为切入点,通过动态设置的流量控制、服务熔断等手段达到保护系统的目的,通过服务降级增强服务被拒用户的体验。

    2 服务降级

    服务降级是一种增强用户体验的方式。当用户的请求由于各种原因被拒后,系统返回一个事先设定好的、用户可以接受的,但又令用户并不满意的结果。这种请求处理方式称为服 务降级。
    案例完整源码

    2.1 降级实现方式分类

    对于 Sentinel,服务降级的实现方式根据消费者类型的不同,其支持两种方式:

    • Sentinel 式降级:通过 Sentinel 自身的 API 实现的降级方式,适用于任意消费者类型。而根据降级方法应用范围、定义位置及可维护性的不同,又可分为两种:
      方法级降级:降级方法与原方法定义在同一个类中,其仅是本类中的原方法可以使用。
      类级降级:降级方法定义在专门的一个类中,其是一个可以被共享的降级类。该类中的所有方法均为降级方法,所以便于维护与管理。
    • Feign 式降级:通过 OpenFeign 的 API 实现的降级方式,仅适用于 Feign 客户端的消费者类型。其只有类级降级方式。

    2.2 Sentinel 式方法级降级

    (1)添加依赖

    在原有的feign-nacos项目中添加依赖

      <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
            <version>2.2.1.RELEASEversion>
          dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (2) 修改处理器类

        /**
         * 定义了降级
         */
        @SentinelResource(fallback = "getHandleFallback")
        @GetMapping("/get/{id}")
        public DepartVO getHandle(@PathVariable("id") int id) {
            return departService.getDepartById(id);
        }
    
        public DepartVO getHandleFallback(int id, Throwable e) {
            DepartVO departVO = new DepartVO();
            departVO.setId(id);
            departVO.setName("degrade-method-" + id + "-" + e.getMessage());
            return departVO;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    (3) 测试

    1. 只启动消费者,访问接口发现走了降级方法。

    在这里插入图片描述

    1. 启动消费者和生产者无异常,正常访问接口
    2. 在消费者端模拟异常并启动,访问接口发现走了降级方法。
      在这里插入图片描述
      思考了一下,这里是为了模拟降级服务而简单的处理。实际中应该会具体实现降级业务,针对异常做些处理。

    2.3 Sentinel 式类级降级

    (1) 定义降级类

    public class DepartServiceFallback {
    
        public static boolean saveFallback(DepartVO depart, Throwable e) {
            System.out.println("getHandle()执行异常 " + depart.toString());
            return false;
        }
    
        public static List<DepartVO> listFallback() {
            System.out.println("listHandle()执行异常 ");
            DepartVO departVO = new DepartVO();
            List<DepartVO> arrayList = new ArrayList<>();
            departVO.setId(1);
            departVO.setName("no any depart");
            arrayList.add(departVO);
            return arrayList;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    (2) 修改 DepartController 处理器

        /**
         *@SentinelResource注解中指定降级方法和降级方法对应的类
         */
        @SentinelResource(fallback = "saveFallback", fallbackClass = DepartServiceFallback.class)
        @PostMapping("/save")
        public boolean saveHandle(@RequestBody DepartVO depart) {
            return departService.saveDepart(depart);
        }
        
        @SentinelResource(fallback = "listFallback", fallbackClass = DepartServiceFallback.class)
        @GetMapping("/list")
        public List<DepartVO> listHandle() {
            return departService.listAllDeparts();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    (3)测试

    只启动消费者
    在这里插入图片描述

    2.4 Feign 式类级降级

    (1)添加依赖

    消费者端项目中添加依赖

      <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
            <version>2.2.1.RELEASEversion>
          dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (2) 修改配置文件

    feign:        
      sentinel: # 开启sentinel对feign的支持
        enabled: true
    
    • 1
    • 2
    • 3

    (3) 定义降级类

    在feign对外接口中添加DepartServiceFallback降级类
    降级类实现feign接口,在方法中根据实际业务完成降级业务
    在这里插入图片描述

    // 降级类:实现了Feign接口
    @Component
    //@RequestMapping("/fallback/provider/depart")  // 其开头必须是/fallback
    public class DepartServiceFallback implements DepartService {
        @Override
        public boolean saveDepart(DepartVO depart) {
            System.out.println("执行saveDepart()的服务降级处理方法");
            return false;
        }
    
        @Override
        public boolean removeDepartById(int id) {
            System.out.println("执行removeDepartById()的服务降级处理方法");
            return false;
        }
    
        @Override
        public boolean modifyDepart(DepartVO depart) {
            System.out.println("执行modifyDepart()的服务降级处理方法");
            return false;
        }
    
        @Override
        public DepartVO getDepartById(int id) {
            System.out.println("执行getDepartById()的服务降级处理方法");
            DepartVO depart = new DepartVO();
            depart.setId(id);
            depart.setName("degrade-feign");
            return depart;
        }
    
        @Override
        public List<DepartVO> listAllDeparts() {
            System.out.println("执行listAllDeparts()的服务降级处理方法");
            return null;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    (4)修改feign接口

    //在注解中指定降级类
    @FeignClient( value="feign-nacos-provider-modules", path = "/provider/depart",
    fallback = DepartServiceFallback.class)
    public interface DepartService {
        @PostMapping("/save")
        boolean saveDepart(@RequestBody DepartVO depart);
    
        @DeleteMapping("/del/{id}")
        boolean removeDepartById(@PathVariable("id") int id);
    
        @PutMapping("/update")
        boolean modifyDepart(@RequestBody DepartVO depart);
    
        @GetMapping("/get/{id}")
        DepartVO getDepartById(@PathVariable("id") int id);
    
        @GetMapping("/list")
        List<DepartVO> listAllDeparts();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    (5)测试

    只启动消费者,模拟生产者异常。访问删除接口。结果发现执行了降级方法
    在这里插入图片描述

    3 Sentinel Dashboard

    Sentinel 控制台(官方链接)

    3.1 简介

    Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。

    Sentinel 控制台包含如下功能:

    • 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
    • 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
    • 规则管理和推送:统一管理推送规则。
    • 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。

    注意:Sentinel 控制台目前仅支持单机部署。Sentinel 控制台项目提供 Sentinel 功能全集示例,不作为开箱即用的生产环境控制台,不提供安全可靠保障。若希望在生产环境使用请根据文档自行进行定制和改造。

    3.2 下载

    直接从官方下载打包好的 Sentinel Dashboard 启动运行

    在这里插入图片描述
    组件版本关系
    Sentinel Dashboard 版本需要和Spring Cloud Alibaba Version对应,项目中Spring Cloud Alibaba Version为2.2.1.RELEASE,我们选用1.7.1以上版本。1.8版本和1.7版本界面变化有些大,选用新一点版本使用1.8.0下载
    在这里插入图片描述
    在这里插入图片描述

    3.3 启动

    启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本
    在这里插入图片描述

    java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 
    -Dproject.name=sentinel-dashboard 
    -Dsentinel.dashboard.auth.username=sentinel 
    -Dsentinel.dashboard.auth.password=sentinel -jar sentinel-dashboard-1.8.0.jar
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    3.4 访问

    http://localhost:8888/#/login
    在这里插入图片描述

    在这里插入图片描述

    4 服务熔断

    项目案例源码

    4.1熔断概念

    服务雪崩和熔断介绍

    (1) 服务雪崩
    如果说服务流控是为了在高并发场景下不至于将系统压垮,那么服务熔断则是为了在外部环境不通畅场景下,不至于将系统拖垮,也就是为了防止服务雪崩的发生。
    在这里插入图片描述

    在复杂的系统中,经常会出现 A 依赖于 B,B 依赖于 C,C 依赖于 D,……这种依赖将会产生很长的调用链路,这种复杂的调用链路称为 1->N 的扇出。
    如果在 A 的调用链路上某一个或几个被调用的子服务不可用或延迟较高,则会导致调用A 服务的请求被堵住(等待超时)。堵住的 A 请求会消耗占用系统的线程、IO 等资源,当对A 服务的请求越来越多,占用的计算机资源越来越多的时候,会导致系统瓶颈出现,造成其他的请求同样不可用,最终导致业务系统崩溃,这种现象称为雪崩效应

    (2) 服务熔断
    Hystrix官网
    为了防止服务雪崩的发生,在发现了对某些资源请求的响应缓慢或调用异常较多时,直接将对这些资源的请求掐断一段时间。而在这段时间内的请求将不再等待超时,而是直接返回事先设定好的降级结果。这些请求将不占用系统资源,从而避免了服务雪崩的发生。这就是服务熔断。

    4.2 动态设置

    对于服务熔断规则,可以通过 Sentinel Dashboard 进行动态设置。

    4.2.1 修改处理器类

    在消费者类处理器上配置降级名称,后续降级名称需要在sentinel dashboard中使用。这样客户端上配置的降级规则才能够知道给谁使用。

        /**
         * 定义了降级
         */
        @SentinelResource(value = "slowRequstDegradeRule",fallback = "getHandleFallback")
        @GetMapping("/get/{id}")
        public DepartVO getHandle(@PathVariable("id") int id) {
            return departService.getDepartById(id);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    4.2.2 修改配置文件

    spring:
      application:
        name: feign-nacos-consumer  # 微服务名称
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848   # nacos discovery地址
        sentinel:
          transport:
            dashboard: localhost:8888  # Sentinel客户端地址
            port: 8197 # 服务和客户端通信端口,在客户端上配置会生效到服务上需要发送请求
          eager: true # 开启配置后立即生效
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    4.2.3 在sentinel 客户端设置降级规则

    先启动消费者服务,在进入sentinel控制台。
    在这里插入图片描述

    4.2.4 修改提供者工程

    设置睡眠时间,模拟接口请求慢。设置完成启动提供者

        @GetMapping("/get/{id}")
        public DepartVO getHandle(@PathVariable("id") int id) throws InterruptedException {
            TimeUnit.MILLISECONDS.sleep(500);
            DepartVO departVO = new DepartVO();
            departVO.setId(1);
            departVO.setName("liming");
            return departVO;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    4.2.5 测试

    1. 单次访问,可以发现能够正常取值。

    http://localhost:8080/consumer/depart/get/1
    在这里插入图片描述

    1. 在浏览器中快速按F5不断刷新接口,连续刷新8次
      注意 我们使用的sentinel版本是1.7.1的,这个版本不支持修改比例阈值(默认为1),sentinel 客户端是1.8.0界面上虽然有比例阈值修改,但是不生效的。
      在这里插入图片描述

    3.紧接着再次刷新接口

    还是返回的降级结果。设置了熔断时长为20s,在这20秒访问会直接走降级逻辑。
    
    • 1

    4.3 熔断策略

    (1)响应时间 RT

    在这里插入图片描述

    慢调用比。该策略需要设置用于界定慢调用的响应时间阈值 RT(Response Time),
    当请求的响应时间大于该值时,将该请求统计为慢调用。若要发生熔断,
    在 1 秒内收到的请求数 量不能小于“最小请求数”,且慢调用占比不能低于“比例阈值”。
    当触发熔断,则会在“熔断时长”内不再对请求进行处理,即熔断期间再来的请求,
    将直接进行降级响应。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (2)异常比

    在这里插入图片描述

    异常比。该策略需要设置异常请求在统计时间窗口内所有请求中的占比,异常比率的阈 
    值范围是 [0.0, 1.0],代表 0% - 100%。当异常请求比例大于该值时则会触发熔断。
    默认情况下,统计时间窗口大小为 1 秒,期间接收到的请求至少 5 个。
    发生熔断后,熔断时长为指定的时长。熔断期间再来的请求,将直接地降级响应。
    
    • 1
    • 2
    • 3
    • 4

    (3)异常数

    在这里插入图片描述

    异常数。该策略需要设置在统计时间窗口内所接收到的异常请求的数量。
    当异常请求数量大于该值时则会触发熔断。默认情况下,统计时间窗口大小为 1 分钟,
    注意,是 1 分钟。发生熔断后,熔断时长为指定的时长。熔断期间再来的请求,
    将直接地降级响应。
    
    • 1
    • 2
    • 3
    • 4

    4.4 代码设置

    通过 Dashboard 平台进行熔断规则设置,粒度有些粗,有些属性只能通过代码来设置。而代码中的 API,在不同的 Sentinel 版本中,是有所不同的。
    熔断规则直接定义在代码中,当应用启动时完成熔断规则的创建与初始化。这个熔断规则在 Dashboard 中也是可以查看到并且进行编辑的,编辑后以动态编辑的规则为准。

    添加降级规则配置类

    @Configuration
    public class hystrixRuleConfig {
    
        /**
         * 初始化生效的降级规则
         */
        @Bean
        public void initRule() {
            List<DegradeRule> rules = new ArrayList<>();
            rules.add(slowRequestDegradeRule());
            rules.add(errorRatioDegradeRule());
            rules.add(errorCountDegradeRule());
            DegradeRuleManager.loadRules(rules);
        }
    
        /**
         * 慢调用
         */
        @Bean
        public DegradeRule slowRequestDegradeRule() {
            //定义降级规则
            DegradeRule degradeRule = new DegradeRule();
            //指定降级规则名称
            degradeRule.setResource("slowRequestDegradeRule");
            //指定熔断策略为Rt(response time)
            degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
            //指定熔断阈值为200ms(请求响应时长大于200ms,统计为慢调用)
            degradeRule.setCount(200);
            //指定熔断时长60s
            degradeRule.setTimeWindow(60);
            //指定在1秒的统计时间窗内至少要5个请求
            degradeRule.setMinRequestAmount(5);
            //指定若要启动熔断,则在1秒内至少要3次慢请求
            degradeRule.setRtSlowRequestAmount(3);
            return degradeRule;
        }
    
        /**
         * 异常比
         */
        @Bean
        public DegradeRule errorRatioDegradeRule() {
            DegradeRule degradeRule = new DegradeRule();
            //指定降级规则名称
            degradeRule.setResource("errorRatioDegradeRule");
            //指定熔断策略为异常比
            degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
            //指定熔断阈值为50%
            degradeRule.setCount(0.5);
            //指定熔断时长60s
            degradeRule.setTimeWindow(60);
            //指定在1秒的统计时间窗内至少要5个请求
            degradeRule.setMinRequestAmount(5);
            return degradeRule;
        }
    
        /**
         * 异常数
         */
        @Bean
        public DegradeRule errorCountDegradeRule() {
            DegradeRule degradeRule = new DegradeRule();
            degradeRule.setResource("errorCountDegradeRule");
            //指定熔断策略为异常数
            degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
            //指定熔断阈值为3
            degradeRule.setCount(3);
            //指定熔断时长60s
            degradeRule.setTimeWindow(60);
            //指定在1秒的统计时间窗内至少要5个请求
            degradeRule.setMinRequestAmount(5);
            return degradeRule;
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    使用降级

    启动消费者服务后我们观察一下控制台,发现三个降级规则都已成功注册。
    无测试异常在这里插入图片描述
    连续发送三次请求
    在这里插入图片描述

    5 服务流控

    项目案例源码

    5.1 流控概念

    流控,即流量控制,也称为限流。Sentinel 实现流控的原理是监控应用流量的 QPS 或并 发线程数等指标,当达到指定的阈值时对再来的请求进行进行控制,以避免被瞬时的流量高 峰冲垮,从而保障应用的高可用性。

    5.2 动态设置

    流控规则直接通过 Sentinel Dashboard 定义,该规则可以随时修改而不需要重启应用该 规则的应用程序。所以这种流控是一种动态流控。

    (1) 修改配置文件

    在这里插入图片描述

    server:
      port: 8080
    spring:
      application:
        name: feign-nacos-consumer  # 微服务名称
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848   # nacos discovery地址
        sentinel:
          transport:
            dashboard: localhost:8888  # Sentinel客户端地址
            port: 8197 # 服务和客户端通信端口,在客户端上配置会生效到服务上需要发送请求
          eager: true # 开启配置后立即生效
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    (2)修改处理器 1-指定流控规则

    在这里插入图片描述

     /**
         * 定义了降级,和限流
         */
        @SentinelResource(value = "qpsFlowRule",
                blockHandler = "getBlockHandler",
                fallback = "getHandleFallback")
        @GetMapping("/get/{id}")
        public DepartVO getHandle(@PathVariable("id") int id) {
            return departService.getDepartById(id);
        }
    
        public DepartVO getBlockHandler(int id, BlockException e) {
            DepartVO departVO = new DepartVO();
            departVO.setId(id);
            departVO.setName("flow-control" + id + "-" + e.getMessage());
            return departVO;
        }
    
        public DepartVO getHandleFallback(int id, Throwable e) {
            DepartVO departVO = new DepartVO();
            departVO.setId(id);
            departVO.setName("degrade-method-" + id + "-" + e.getMessage());
            return departVO;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    (3) dashboard 设置流控规则

    在这里插入图片描述
    每秒访问量超过一次时,就会立刻进入限流函数。

    (4)测试

    http://localhost:8080/consumer/depart/get/1
    在这里插入图片描述

    5.3 代码设置

    (1)添加限流配置类

    @Configuration
    public class flowRuleConfig {
    
        @Bean
        public void initFlowRule(){
            List<FlowRule> flowRules=new ArrayList<>();
            flowRules.add(qpsFlowRule());
            FlowRuleManager.loadRules(flowRules);
        }
    
        @Bean
        public FlowRule qpsFlowRule(){
            FlowRule flowRule = new FlowRule();
            flowRule.setResource("qpsFlowRule");
            flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            flowRule.setCount(1);
            flowRule.setLimitApp("default");
            return flowRule;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    测试

    项目重新启动,限流配置会注册到控制台在这里插入图片描述

  • 相关阅读:
    国内做SRM系统的公司哪家比较好?如何利用SRM系统管理好供应商?
    思维导图解读《纳瓦尔宝典》
    Ribbon 添加右侧区域菜单项
    第4章 系统管理员模块功能实现
    Azure 基础
    自定义类型转换函数operator MyInt()
    力扣 827. 最大人工岛
    Eureka详解
    k8s从入门到精通
    教官保护手法基础要点
  • 原文地址:https://blog.csdn.net/weixin_43811057/article/details/126727820