SpringCloud-2.0-周阳(23. 熔断降级 - Sentinel)

Posted ABin-阿斌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud-2.0-周阳(23. 熔断降级 - Sentinel)相关的知识,希望对你有一定的参考价值。

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

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

  • 声明:原文作者:csdn:yuan_404

文章目录

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 注解内属性,有时效果不好
  • 所以需改了该注解中的内容,最好还是重启