SpringCloud-2.0-周阳(23. 熔断降级 - Sentinel)学习笔记

上一篇 :22. 流量监控 - Sentinel

下一篇 :24. 分布式事务 - Seata

1. 降级规则

1.1 介绍

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

1.2 慢调用比例(RT)

  1. 先在 Controller 中添加一个方法

    @GetMapping("/testD")
    public String testD(){
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
        log.info("testD 测试RT");
    
        return "------testD";
    }
    
  2. 设置降级规则
    在这里插入图片描述
    在这里插入图片描述

  3. 使用 JMeter 进行压测
    在这里插入图片描述
    在这里插入图片描述

  4. 启动 JMeter ,去浏览器上访问该请求,查看结果
    在这里插入图片描述

  5. 停止 JMeter 后,再访问该请求
    在这里插入图片描述

1.3 异常比例

  1. 修改 Controller ,添加一个运行时异常
    在这里插入图片描述
  2. 修改 降级规则
    在这里插入图片描述
  3. 启动 JMeter ,去浏览器上访问该请求,查看结果
    在这里插入图片描述
  4. 停止 JMeter 后,再访问该请求
    在这里插入图片描述

1.4 异常数

  1. 修改 降级规则
    在这里插入图片描述
  2. 连续发出请求,当刷新第六次的时候被熔断了
    在这里插入图片描述
  3. 过一段时间后再次访问
    在这里插入图片描述

2. @SentinelResource 详解

2.1 按资源名称限流 + 备选方案

  • 启动 Nacos 控制台
  • 启动 Sentinel 控制台

2.1.1 环境搭建

  1. 修改模块 :8401

  2. 修改 POM

    <dependency>
        <groupId>com.demo.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>
    
  3. 新建 Controller :RateLimitController

    @RestController
    public class RateLimitController {
        @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 服务不可用");
        }
    }
    
  4. 启动项目

  5. 访问 :http://localhost:8401/byResource
    在这里插入图片描述

2.1.2 配置流控规则

在这里插入图片描述

  • 当每秒访问该资源的请求数大于 1,则限流
    在这里插入图片描述

2.1.3 额外问题

  • 这时突然关闭 8401 服务,流控规则是否会消失?

在这里插入图片描述

2.2 按照Url地址限流 + 备选方案

  • 修改 RateLimitController

    @GetMapping("/rateLimit/byUrl")
    @SentinelResource(value = "byUrl")
    public CommonResult byUrl() {
        return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
    }
    

    在 Controller 中添加这样一个方法

    没有配置 blockHandler 限流之后的处理方法

  • 启动 8401

  • 访问 :http://localhost:8401/rateLimit/byUrl
    在这里插入图片描述

  • 配置流控规则
    在这里插入图片描述

  • 这里是将请求的 Url 作为限流的资源名
    在这里插入图片描述

2.3 上面 两个配置备选方案的方式 的问题

  1. 系统默认的,没有体现我们自己的业务要求。

  2. 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。

  3. 每个业务方法都添加一个兜底的,那代码膨胀加剧。

  4. 全局统—的处理方法没有体现。

  • 下面就来尝试解决一下这些 代码耦合、膨胀的问题

2.4 自定义限流处理逻辑

  • 修改 alibaba-sentinel-service-8401
  1. 自定义一个限流处理类 :CustomerBlockHandler

    public class CustomerBlockHandler {
        public static CommonResult handleException(BlockException exception) {
            return new CommonResult(2020, "自定义限流处理信息....CustomerBlockHandler");
        }
    }
    
  2. 修改 Controller :RateLimitController

    @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class,
            blockHandler = "handlerException2")
    public CommonResult customerBlockHandler() {
        return new CommonResult(200,"客戶自定义 Handler",new Payment(2020L,"serial003"));
    }
    

    在这里插入图片描述

  3. 重启 8401

  4. 访问 :http://localhost:8401/rateLimit/customerBlockHandler
    在这里插入图片描述

  5. 配置限流规则
    在这里插入图片描述

  6. 连续访问 :http://localhost:8401/rateLimit/customerBlockHandler

    在这里插入图片描述

2.5 @SentinelResource 注解属性说明

  • value:资源名称,必需项(不能为空)

  • entryType:entry 类型,可选项(默认为 EntryType.OUT)

  • blockHandler: 处理BlockException的函数名称。函数要求:

    1、必须是 public
    2、返回类型与原方法一致
    3、参数类型需要和原方法相匹配,并在最后加 BlockException 类型的参数。
    4、默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法。

  • blockHandlerClass :存放blockHandler的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同blockHandler。

  • fallback :fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:

    1、返回值类型必须与原函数返回值类型一致;
    2、方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    3、fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

  • exceptionsToTrace :需要trace的异常

3. 服务熔断功能

  • 先启动 Nacos 和 Sentinel

3.1 Ribbon系列

  • 避免和前面的知识点混淆,这里重新创建一套环境

3.1.1 提供者 Provider-9003

  1. 新建模块 :alibaba-provider-Sentinel-Ribbon-9003

  2. 修改 POM

    <dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.demo.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <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>
    
  3. 编写 YML

    server:
      port: 9003
    
    spring:
      application:
        name: nacos-payment-provider
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 #配置Nacos地址
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    
  4. 编写主启动类

    @SpringBootApplication
    @EnableDiscoveryClient
    public class ProviderMain9003 {
        public static void main(String[] args) {
            SpringApplication.run(ProviderMain9003.class, args);
        }
    }
    
  5. 业务类

  • Controller

    @RestController
    public class ProviderController {
        @Value("${server.port}")
        private String serverPort;
    
        public static HashMap<Long, Payment> hashMap = new HashMap<>();
        static{
            hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
            hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
            hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
        }
    
        @GetMapping(value = "/paymentSQL/{id}")
        public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
            Payment payment = hashMap.get(id);
            CommonResult<Payment> result = new CommonResult(200,"from mysql,serverPort:  "+serverPort,payment);
            return result;
        }
    }
    
  1. 测试

    启动 Sentinel、Nacos
    启动 Provider-9003
    访问 :http://localhost:9003/paymentSQL/1
    在这里插入图片描述

  • Provider-9003 搭建成功,复制配置,克隆出来一个 9004,具体操作之前有说过这就不多说了。
    在这里插入图片描述
    在这里插入图片描述

3.1.2 消费者 Consumer-84

  1. 新建模块 :alibaba-consumer-Sentinel-Ribbon-84

  2. 修改 POM

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.demo.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <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>
        <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>
    
  3. 编写 YML

    server:
      port: 84
    
    spring:
      application:
        name: nacos-order-consumer
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
        sentinel:
          transport:
            dashboard: localhost:8080
            port: 8719
    
    service-url:
      nacos-user-service: http://nacos-payment-provider
    
  4. 编写主启动类

    @SpringBootApplication
    @EnableDiscoveryClient
    public class ConsumerMain84 {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerMain84.class, args);
        }
    }
    
  5. 业务类

  • Config :ApplicationContextConfig

    @Configuration
    public class ApplicationContextConfig {
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate(){
            return new RestTemplate();
        }
    }
    
  • Controller

    @RestController
    public class CircleBreakerController {
        @Value("${service-url.nacos-user-service}")
        private String SERVER_URL;
    
        @Autowired
        private RestTemplate restTemplate;
    
        @GetMapping(value = "/consumer/paymentSQL/{id}")
        @SentinelResource(value = "fallback")
        public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
            return restTemplate.getForObject(SERVER_URL+"/paymentSQL/"+id, CommonResult.class);
        }
    }
    
  1. 测试
    访问 :http://localhost:84/consumer/paymentSQL/3
    在这里插入图片描述

3.1.3 熔断降级配置

  • 注意 :
  • 热部署对java代码级生效及时
  • 对 @SentinelResource 注解内属性,有时效果不好
  • 所以需改了该注解中的内容,最好还是重启
  • 在之前的 Consumer 的 Controller 中添加一段异常捕获的代码

    // 异常捕捉
    if (id == 4) {
        throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
    }else if (result.getData() == null) {
        throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
    }
    

    在这里插入图片描述

  • 访问 :http://localhost:84/consumer/paymentSQL/4

    因为之前在 Provider 中只定义了三条数据,没有 Id 为 4 的数据,所以肯定报错。

    在这里插入图片描述

  • 现在没有配置任何备选方案,直接把异常的代码展示给用户,很不友好。

只配置 fallback
  • 下面开始配置,fallback
     @SentinelResource(value = "fallback", fallback = "handlerFallBack") // fallback 只负责业务异常
    
    /** FallBack 备选方案 */
    public CommonResult handlerFallBack(@PathVariable("id") Long id, Throwable e){
        Payment payment = new Payment(id, "null");
        return new CommonResult<Payment>(444, "备选方案 —— handlerFallBack,异常内容" + e.getMessage(), payment);
    }
    
  • 重启 Consumer-84 ,还访问 :http://localhost:84/consumer/paymentSQL/4
    在这里插入图片描述
只配置 blockHandler
  • 下面开始配置,blockHandler

    // blockHandler 只负责 Sentinel 控制台配置的熔断降级规则
        @SentinelResource(value = "fallback", blockHandler = "blockHandler") 
    
    /** 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);
    }	
    
  • 重启 Consumer-84

  • 配置 Sentinel 降级规则
    在这里插入图片描述

  • 多次访问 :http://localhost:84/consumer/paymentSQL/4
    在这里插入图片描述
    在这里插入图片描述

同时配置 fallBack 和 blockHandler
  • 下面开始配置

    @SentinelResource(value = "fallback", fallback = "handlerFallBack", blockHandler = "blockHandler")
    

    在这里插入图片描述

  • 重启 Consumer-84

  • 配置 Sentinel 降级规则
    在这里插入图片描述

  • 多次访问 :http://localhost:84/consumer/paymentSQL/4
    在这里插入图片描述
    在这里插入图片描述

配置 exceptionsToIgnore

3.2 Feign系列

3.2.1 环境配置

  • 修改 Consumer-84 模块
  1. 修改 POM

    添加 OpenFeign 依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  2. 修改 YML

    添加 Sentinel 对 Feign 的支持

    feign:
      sentinel:
        enabled: true
    
  3. 修改主启动类

    // 开启对 Feign 的支持
    @EnableFeignClients
    

    在这里插入图片描述

  4. 添加业务类

  • Feign 的业务接口 :FeignService

    @FeignClient(value = "nacos-payment-provider", fallback = FeignFallbackService.class)
    public interface FeignService {
        @GetMapping(value = "/paymentSQL/{id}")
        public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
    }
    
  • 对应的 FallBack 的 Service :FeignFallbackService

    @Component
    public class FeignFallbackService implements FeignService {
        @Override
        public CommonResult<Payment> paymentSQL(Long id) {
            return new CommonResult<>(44444,"服务降级返回,---FeignFallbackService",new Payment(id,"errorSerial"));
        }
    }
    
  • Controller :FeignController

    @RestController
    public class FeignController {
        @Autowired
        private FeignService feignService;
    
        @GetMapping(value = "/consumer/feign/{id}")
        public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
            return feignService.paymentSQL(id);
        }
    }
    
  1. 启动测试

    访问 :http://localhost:84/consumer/feign/1
    在这里插入图片描述

3.3 熔断降级框架对比

SentinelHystrixResilience4j
隔离策略信号量隔离(并发线程数限流)线程池隔离 / 信号量隔离信号量隔离
熔断降级策略基于响应时间、异常比、异常数基于异常比基于异常比、响应时间
实时统计实现滑动窗口(LeapArray)滑动窗口(RxJava)Ring Bit Buffer
动态规则配置支持多种数据源支持多种数据源有限支持
扩展性多个扩展点插件形式扩展接口形式扩展
限流基于 QPS、支持基于调用关系的限流有限支持Rate Limter
流量整形支持预热、匀加速、预热排队模式不支持简单的 Rate Limiter
系统自适应保护支持不支持不支持
控制台提供开箱即用的控制台,可以配置规则、查看妙计监控、机器发现等简单的监控查看不提供控制台,可对接其他监控系统
基于注解的支持支持支持支持

4. Sentinel 规则持久化

  • 一旦我们重启应用,Sentinel规则将消失
  • 生产环境需要将配置规则进行持久化
  • 将限流配置规则持久化进 Nacos 保存
  • 只要刷新 8401 某个 rest 地址,sentinel 控制台的流控规则就能看到,只要 Nacos 里面的配置不删除,针对 8401 上 Sentinel 上的流控规则持续有效
  • 具体使用步骤 :
  • 修改 alibaba-sentinel-service-8401
  1. 修改 POM,添加相关依赖

    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    
  2. 修改 YML,添加 Nacos 业务规则配置

    datasource:
      ds1:
        nacos:
          server-addr: localhost:8848
          dataId: cloudalibaba-sentinel-service
          groupId: DEFAULT_GROUP
          data-type: json
          rule-type: flow
    

    在这里插入图片描述

  3. 添加 Nacos 业务规则配置

    配置内容 :

    [
        {
             "resource": "/retaLimit/byUrl",
             "limitApp": "default",
             "grade":   1,
             "count":   1,
             "strategy": 0,
             "controlBehavior": 0,
             "clusterMode": false    
        }
    ]
    

    在这里插入图片描述
    在这里插入图片描述

  4. 启动 alibaba-sentinel-service-8401、Sentinel

  5. 请求 :http://localhost:8401/rateLimit/byUrl

  6. 然后去 Sentinel 控制台刷新,查看流控规则
    在这里插入图片描述
    在这里插入图片描述

  • 停止 8401 服务,刷新 Sentinel,刚刚的流控规则消失了
    在这里插入图片描述
  • 再次启动 8401 ,刷新 Sentinel,刚刚的流控规则又回来了
    在这里插入图片描述
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yuan_404

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值