• SpringCloudAlibaba-2.熔断、降级与限流(Sentinel)


    目录

    一、概念

    1.1 介绍

    1.2 快速配置

    二、基础功能演示

    三、流控规则

    四、降级规则

    五、@SentinelResource注解

    5.1 按资源名限流演示

    5.2 自定义限流处理类

    六、热点Key限流

    6.1 基本演示

    6.2 热点规则添加例外项

    七、系统规则

    八、服务熔断功能(+ribbon/openFeign)

    8.1 sentinel+ribbon

    8.2 sentinel+openFeign

    九、规则持久化


    一、概念

    1.1 介绍

            Sentinel是一个轻量级的流量控制熔断降级Java库。类似于Hystrix。可以处理服务使用过程中的各种问题,如服务雪崩服务降级服务熔断服务限流等。

    1.2 快速配置

            下载:下载jar包,例如sentinel-dashboard-1.7.0.jar

            使用前提Jdk88080端口未被占用。

            开启:在jar包目录下执行下面指令。

    java -jar sentinel-dashboard-1.7.0.jar

            最后输入地址:        http://localhost:8080        即为Sentinel的后台管理界面

    二、基础功能演示

            1.建立新工程-cloudalibaba-sentinel-service8401

            2.POM

    1. <dependencies>
    2. <dependency>
    3. <groupId>com.alibaba.cloudgroupId>
    4. <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    5. dependency>
    6. <dependency>
    7. <groupId>com.alibaba.cspgroupId>
    8. <artifactId>sentinel-datasource-nacosartifactId>
    9. dependency>
    10. <dependency>
    11. <groupId>com.alibaba.cloudgroupId>
    12. <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
    13. dependency>
    14. <dependency>
    15. <groupId>org.springframework.cloudgroupId>
    16. <artifactId>spring-cloud-starter-openfeignartifactId>
    17. dependency>
    18. <dependency>
    19. <groupId>org.springframework.bootgroupId>
    20. <artifactId>spring-boot-starter-webartifactId>
    21. dependency>
    22. <dependency>
    23. <groupId>org.springframework.bootgroupId>
    24. <artifactId>spring-boot-starter-actuatorartifactId>
    25. dependency>
    26. <dependency>
    27. <groupId>org.springframework.bootgroupId>
    28. <artifactId>spring-boot-devtoolsartifactId>
    29. <scope>runtimescope>
    30. <optional>trueoptional>
    31. dependency>
    32. <dependency>
    33. <groupId>cn.hutoolgroupId>
    34. <artifactId>hutool-allartifactId>
    35. <version>4.6.3version>
    36. dependency>
    37. <dependency>
    38. <groupId>org.projectlombokgroupId>
    39. <artifactId>lombokartifactId>
    40. <optional>trueoptional>
    41. dependency>
    42. <dependency>
    43. <groupId>org.springframework.bootgroupId>
    44. <artifactId>spring-boot-starter-testartifactId>
    45. <scope>testscope>
    46. dependency>
    47. dependencies>

            3.XML

    1. server:
    2. port: 8401
    3. spring:
    4. application:
    5. name: cloudalibaba-sentinel-service
    6. cloud:
    7. nacos:
    8. discovery:
    9. #Nacos服务注册中心地址
    10. server-addr: localhost:8848
    11. sentinel:
    12. transport:
    13. #配置Sentinel dashboard地址
    14. dashboard: localhost:8080
    15. #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
    16. port: 8719
    17. management:
    18. endpoints:
    19. web:
    20. exposure:
    21. include: '*'

            4.主启动

    1. @EnableDiscoveryClient
    2. @SpringBootApplication
    3. public class MainApp8401
    4. {
    5. public static void main(String[] args) {
    6. SpringApplication.run(MainApp8401.class, args);
    7. }
    8. }

            5.业务类

    1. @RestController
    2. public class FlowLimitController
    3. {
    4. @GetMapping("/testA")
    5. public String testA()
    6. {
    7. return "------testA";
    8. }
    9. @GetMapping("/testB")
    10. public String testB()
    11. {
    12. return "------testB";
    13. }
    14. }

            6.测试

                    首先确保启动了NacosSentinel服务。

                    连续输入:        localhost:8401/testA   localhost:8401/testB

                    Sentinel 后台的数据:(QPS-每秒的请求数

    三、流控规则

    四、降级规则

     RT(平均响应时间,秒级)
          平均响应时间超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级
          窗口期过后关闭断路器
          RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)


    异常比列(秒级)
        QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级


    异常数(分钟级)
         异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级


            Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
            当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。 

            Sentinel断路器没有半开状态

    五、@SentinelResource注解

            简单来说,@SentinelResource 用来标识资源名以及定义违反规则代码出错时的兜底方法,类似于HystrixCommand 定义FallBack。系统默认有兜底方法,默认提示:Blocked by Sentinel (flow limiting)

    5.1 按资源名限流演示

            还是在上面的 cloudalibaba-sentinel-service8401

                    1.POM引入自定义包

    1. <dependency>
    2. <groupId>com.atguigu.springcloudgroupId>
    3. <artifactId>cloud-api-commonsartifactId>
    4. <version>${project.version}version>
    5. dependency>

                    2.新建Controller

    1. @RestController
    2. public class RateLimitController
    3. {
    4. @GetMapping("/byResource")
    5. @SentinelResource(value = "byResource",blockHandler = "handleException")
    6. public CommonResult byResource()
    7. {
    8. return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
    9. }
    10. public CommonResult handleException(BlockException exception)
    11. {
    12. return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
    13. }
    14. }

                     3.配置流控规则

             4.测试

                    输入:      localhost:8401/byResource   频次大于1时,会返回我们自定义的错误信息。

            注:最好按照资源名设置流控规则,如果按照URL设置,只会使用默认兜底方法

    5.2 自定义限流处理类

            上面方式的问题:

    1  系统默认的,没有体现我们自己的业务要求。
    2  依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。 
    3  每个业务方法都添加一个兜底的,那代码膨胀加剧。 
    4  全局统一的处理方法没有体现。

            1.自定义限流处理类- CustomerBlockHandler

    1. public class CustomerBlockHandler
    2. {
    3. public static CommonResult handleException(BlockException exception){
    4. return new CommonResult(2020,"自定义的限流处理信息......CustomerBlockHandler");
    5. }
    6. }

            2.修改Controller

    1. @GetMapping("/rateLimit/customerBlockHandler")
    2. @SentinelResource(value = "customerBlockHandler",
    3. blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException")
    4. public CommonResult customerBlockHandler()
    5. {
    6. return new CommonResult(200,"按客户自定义限流处理逻辑");
    7. }

             3.测试

                    设置流控规则

                输入: 

        localhost:8401/rateLimit/customerBlockHandler  频次大于1时,会返回我们自定义的错误信息。

    六、热点Key限流

            热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作        

    6.1 基本演示

            1.修改Controller

    1. @GetMapping("/testHotKey")
    2. @SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")
    3. public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
    4. @RequestParam(value = "p2",required = false) String p2){
    5. return "------testHotKey";
    6. }
    7. public String dealHandler_testHotKey(String p1,String p2,BlockException exception)
    8. {
    9. return "-----dealHandler_testHotKey";
    10. }

            2.设置热点规则

            方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理

            3.测试

                    输入:        http://localhost:8401/testHotKey?p1=abc        频率超过1,报错

                    输入:        http://localhost:8401/testHotKey?p1=abc&p2=33   频率超过1,报错

                    输入:        http://localhost:8401/testHotKey?p2=abc        频率超过1,不报错

    6.2 热点规则添加例外项

            上面的示例中,参数p1当QPS超过1秒1次点击后马上被限流,假如我们期望p1参数是某个特殊值时,它的限流阈值和平时不一样。(就是在特定条件下允许例外),可以这样设置:

             注:热点参数的注意点,参数必须是基本类型或者String

    七、系统规则

             这个设置要慎重,因为全局的,影响到整个服务节点。不再演示

    八、服务熔断功能(+ribbon/openFeign

            有两种方案:sentinel+ribbon / sentinel+openFeign

    8.1 sentinel+ribbon

            1.新建cloudalibaba-provider-payment9003/9004两个一样的做法,只是端口不同

            2.POM

    1. <dependencies>
    2. <dependency>
    3. <groupId>com.alibaba.cloudgroupId>
    4. <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    5. dependency>
    6. <dependency>
    7. <groupId>com.atguigu.springcloudgroupId>
    8. <artifactId>cloud-api-commonsartifactId>
    9. <version>${project.version}version>
    10. dependency>
    11. <dependency>
    12. <groupId>org.springframework.bootgroupId>
    13. <artifactId>spring-boot-starter-webartifactId>
    14. dependency>
    15. <dependency>
    16. <groupId>org.springframework.bootgroupId>
    17. <artifactId>spring-boot-starter-actuatorartifactId>
    18. dependency>
    19. <dependency>
    20. <groupId>org.springframework.bootgroupId>
    21. <artifactId>spring-boot-devtoolsartifactId>
    22. <scope>runtimescope>
    23. <optional>trueoptional>
    24. dependency>
    25. <dependency>
    26. <groupId>org.projectlombokgroupId>
    27. <artifactId>lombokartifactId>
    28. <optional>trueoptional>
    29. dependency>
    30. <dependency>
    31. <groupId>org.springframework.bootgroupId>
    32. <artifactId>spring-boot-starter-testartifactId>
    33. <scope>testscope>
    34. dependency>
    35. dependencies>

            3.YML

    1. server:
    2. port: 9003 #记得改
    3. spring:
    4. application:
    5. name: nacos-payment-provider
    6. cloud:
    7. nacos:
    8. discovery:
    9. server-addr: localhost:8848 #配置Nacos地址
    10. management:
    11. endpoints:
    12. web:
    13. exposure:
    14. include: '*'

            4.主启动

    1. @SpringBootApplication
    2. @EnableDiscoveryClient
    3. public class PaymentMain9003
    4. {
    5. public static void main(String[] args) {
    6. SpringApplication.run(PaymentMain9003.class, args);
    7. }
    8. }

            5.业务类

    1. @RestController
    2. public class PaymentController
    3. {
    4. @Value("${server.port}")
    5. private String serverPort;
    6. public static HashMap hashMap = new HashMap<>();
    7. static
    8. {
    9. hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
    10. hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
    11. hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
    12. }
    13. @GetMapping(value = "/paymentSQL/{id}")
    14. public CommonResult paymentSQL(@PathVariable("id") Long id)
    15. {
    16. Payment payment = hashMap.get(id);
    17. CommonResult result = new CommonResult(200,"from mysql,serverPort: "+serverPort,payment);
    18. return result;
    19. }
    20. }

            6.新建 cloudalibaba-consumer-nacos-order84 

            7.POM

    1. <dependencies>
    2. <dependency>
    3. <groupId>com.alibaba.cloudgroupId>
    4. <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    5. dependency>
    6. <dependency>
    7. <groupId>com.alibaba.cloudgroupId>
    8. <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
    9. dependency>
    10. <dependency>
    11. <groupId>com.atguigu.springcloudgroupId>
    12. <artifactId>cloud-api-commonsartifactId>
    13. <version>${project.version}version>
    14. dependency>
    15. <dependency>
    16. <groupId>org.springframework.bootgroupId>
    17. <artifactId>spring-boot-starter-webartifactId>
    18. dependency>
    19. <dependency>
    20. <groupId>org.springframework.bootgroupId>
    21. <artifactId>spring-boot-starter-actuatorartifactId>
    22. dependency>
    23. <dependency>
    24. <groupId>org.springframework.bootgroupId>
    25. <artifactId>spring-boot-devtoolsartifactId>
    26. <scope>runtimescope>
    27. <optional>trueoptional>
    28. dependency>
    29. <dependency>
    30. <groupId>org.projectlombokgroupId>
    31. <artifactId>lombokartifactId>
    32. <optional>trueoptional>
    33. dependency>
    34. <dependency>
    35. <groupId>org.springframework.bootgroupId>
    36. <artifactId>spring-boot-starter-testartifactId>
    37. <scope>testscope>
    38. dependency>
    39. dependencies>

            8.YML

    1. server:
    2. port: 84
    3. spring:
    4. application:
    5. name: nacos-order-consumer
    6. cloud:
    7. nacos:
    8. discovery:
    9. server-addr: localhost:8848
    10. sentinel:
    11. transport:
    12. #配置Sentinel dashboard地址
    13. dashboard: localhost:8080
    14. #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
    15. port: 8719
    16. #消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
    17. service-url:
    18. nacos-user-service: http://nacos-payment-provider

            9.主启动

    1. @EnableDiscoveryClient
    2. @SpringBootApplication
    3. public class OrderNacosMain84
    4. {
    5. public static void main(String[] args) {
    6. SpringApplication.run(OrderNacosMain84.class, args);
    7. }
    8. }

            10.业务类

                    10.1 ApplicationContextConfig

    1. @Configuration
    2. public class ApplicationContextConfig
    3. {
    4. @Bean
    5. @LoadBalanced
    6. public RestTemplate getRestTemplate()
    7. {
    8. return new RestTemplate();
    9. }
    10. }

                    10.2 CircleBreakerController 

            @SentinelResource 设置fallback blockHandler 属性,可以分别处理运行时异常违反sentinel规则兜底方法

    1. @RestController
    2. @Slf4j
    3. public class CircleBreakerController
    4. {
    5. public static final String SERVICE_URL = "http://nacos-payment-provider";
    6. @Resource
    7. private RestTemplate restTemplate;
    8. @RequestMapping("/consumer/fallback/{id}")
    9. @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")
    10. public CommonResult fallback(@PathVariable Long id)
    11. {
    12. CommonResult result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);
    13. if (id == 4) {
    14. throw new IllegalArgumentException ("非法参数异常....");
    15. }else if (result.getData() == null) {
    16. throw new NullPointerException ("NullPointerException,该ID没有对应记录");
    17. }
    18. return result;
    19. }
    20. public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
    21. Payment payment = new Payment(id,"null");
    22. return new CommonResult<>(444,"fallback,无此流水,exception "+e.getMessage(),payment);
    23. }
    24. public CommonResult blockHandler(@PathVariable Long id,BlockException blockException) {
    25. Payment payment = new Payment(id,"null");
    26. return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
    27. }
    28. }

            11.测试

            在sentinel中进行配置:

             首先输入:       http://localhost:84/consumer/fallback/1    正常显示数据

             连续输入上面的地址        blockHandler->

    1. {
    2. "code": 445,
    3. "message": "blockHandler-sentinel限流,无此流水: blockException null",
    4. "data": {
    5. "id": 1,
    6. "serial": "null"
    7. }
    8. }

            再输入:        http://localhost:84/consumer/fallback/4        fallback->

    1. {
    2. "code": 444,
    3. "message": "兜底异常handlerFallback,exception内容 IllegalArgumentException,非法参数异常....",
    4. "data": {
    5. "id": 4,
    6. "serial": "null"
    7. }
    8. }

             注:blockHandler fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。

    8.2 sentinel+openFeign

            1.消费端引入openfeign依赖

    1. <dependency>
    2. <groupId>org.springframework.cloudgroupId>
    3. <artifactId>spring-cloud-starter-openfeignartifactId>
    4. dependency>

            2.修改YML,激活sentinel对feigh的支持

    1. # 激活Sentinel对Feign的支持
    2. feign:
    3. sentinel:
    4. enabled: true

             3.新建业务接口 PaymentService,带@FeignClient注解

    1. @FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
    2. public interface PaymentService
    3. {
    4. @GetMapping(value = "/paymentSQL/{id}")
    5. public CommonResult paymentSQL(@PathVariable("id") Long id);
    6. }

            4.新建异常处理业务类 PaymentFallbackService实现 PaymentService

    1. @Component
    2. public class PaymentFallbackService implements PaymentService
    3. {
    4. @Override
    5. public CommonResult paymentSQL(Long id)
    6. {
    7. return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
    8. }
    9. }

            5.修改Controller

    1. @Resource
    2. private PaymentService paymentService;
    3. @GetMapping(value = "/consumer/paymentSQL/{id}")
    4. public CommonResult paymentSQL(@PathVariable("id") Long id)
    5. {
    6. return paymentService.paymentSQL(id);
    7. }

             6.主启动类添加@EnableFeignClients注解

             7.测试

                 输入:           http://localhost:84/consumer/paymentSQL/1        正常显示

                 此时关闭8003、8004        返回fallback:

    1. {
    2. "code": 44444,
    3. "message": "服务降级返回,---PaymentFallbackService",
    4. "data": {
    5. "id": 4,
    6. "serial": "errorSerial"
    7. }
    8. }

    九、规则持久化

            一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化,将限流配置规则持久化进Nacos保存。

            比如我们希望对cloudalibaba-sentinel-service8401上的规则进行持久化:

            1.POM

    1. <dependency>
    2. <groupId>com.alibaba.cspgroupId>
    3. <artifactId>sentinel-datasource-nacosartifactId>
    4. dependency>

            2.YML

    1. spring:
    2. cloud:
    3. sentinel:
    4. datasource:
    5. ds1:
    6. nacos:
    7. server-addr: localhost:8848
    8. dataId: ${spring.application.name}
    9. groupId: DEFAULT_GROUP
    10. data-type: json
    11. rule-type: flow

            3.Nacos中添加规则配置

    1. [
    2. {
    3. "resource": "/rateLimit/byUrl",
    4. "limitApp": "default",
    5. "grade": 1,
    6. "count": 1,
    7. "strategy": 0,
    8. "controlBehavior": 0,
    9. "clusterMode": false
    10. }
    11. ]

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

            4.测试

                    重启8041服务,发送如下请求:

    http://localhost:8401/rateLimit/byUrl

                    刷新sentinel,可以看到持久化的配置规则了:

     

  • 相关阅读:
    碎煤机crusher
    失败驱动开发
    【Unity Material】02 - Material使用技巧
    JVM调优之StringTable调优
    一文掌握 Java8 Stream 中 Collectors 的 24 个操作
    spring boot 配置文件
    每日一题 1155. 掷骰子等于目标和的方法数(中等,动态规划,前缀和)
    做外贸这么久,为什么一直做不好?
    进程关系~
    Java 对象排序(Object Ordering)
  • 原文地址:https://blog.csdn.net/weixin_62427168/article/details/126546287