• 微服务保护Sentinel(二)-- 热点参数限流、隔离降级


    1.热点参数限流

    之前的限流是统计访问某个资源的所有请求,判断是否超过QPS阈值。而热点参数限流是分别统计参数值相同的请求,判断是否超过QPS阈值。

     

    代表的含义是:

    对hot这个资源的 0(第一个参数)做统计,每1秒相同参数值的请求数不能超过5次。

    在热点参数限流的高级选项中,可以对部分参数设置例外配置:

    注意:

    热点参数限流对默认的SpringMVC资源无效,需要使用@SentinelResource() 注解,以及修改配置文件

    给/order/getId/{orderId}这个资源添加热点参数限流,规则如下:

    默认的热点参数规则是每1秒请求量不超过2

    给215这个参数设置每1秒请求量不超过4

    给216这个参数设置每1秒请求量不超过10

    2.隔离与降级

    虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。而要将这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段了。 不管是线程隔离还是熔断降级,都是对客户端(调用方)的保护。

    2.1.FeignClient整合   

    SpringCloud中,微服务调用都是通过Feign来实现的,因此做客户端保护必须整合Feign和Sentinel。

    1.修改OrderService的application文件,开启Feign的Sentinel功能

    1. # 开启openfeign和sentinel整合
    2. feign.sentinel.enabled=true

    2.给FeignClient编写失败后的降级逻辑

    方式一:FallbackClass,无法对远程调用的异常做处理

    方式二:FallbackFactory,可以对远程调用的异常做处理,本文选择这种 

    3.定义类,实现FallbackFactory:

    1. @Slf4j
    2. @Component
    3. public class ProductFeignFactory implements FallbackFactory {
    4. @Override
    5. public ProductFeign create(Throwable throwable) {
    6. ProductFeign productFeign = new ProductFeign() {
    7. //兜底方案
    8. @Override
    9. public Product getById(Integer pid) {
    10. log.error("远程调用出现问题,执行了兜底方法");
    11. Product product = new Product();
    12. product.setPname("异常:"+throwable.getMessage());
    13. return product;
    14. }
    15. };
    16. return productFeign;
    17. }
    18. }

    4. 为被容器的接口指定容错类 

     

    2.2.Sentinel 线程隔离(舱壁模式)

    线程隔离有两种方式实现: 线程池隔离 信号量隔离(Sentinel默认采用)

    信号量隔离:轻量级,无额外开销。但不支持主动超时,不支持异步调用。适用于高频调用,高扇出。

    线程池隔离:支持主动超时,支持异步调用。但现成的额外开销比较大。适用于低扇出。

    在添加限流规则时,可以选择两种阈值类型:

    QPS:就是每秒的请求数,在快速入门中已经演示过

    线程数:是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现舱壁模式。 

    需求:设置流控规则,线程数不能超过 2。然后利用jemeter测试。

     

    2.3.熔断降级

    熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。

    断路器熔断策略有三种:慢调用、异常比例、异常数  

    1.熔断策略-慢调用 

    慢调用:业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。例如:

    解读:RT超过500ms的调用是慢调用,统计最近10000ms内的请求,如果请求量超过10次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。

    需求:设置降级规则,慢调用的RT阈值为100ms,统计时间为10秒,最小请求数量为5,失败阈值比例为0.5,熔断时长为30s

     

    提示:为了触发慢调用规则,需要修改业务,增加业务耗时: 

    2.熔断策略-异常比例、异常数 

    异常比例或异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。例如: 

    解读:统计最近1000ms内的请求,如果请求量超过10次,并且异常比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。 

    需求:设置降级规则,统计时间为10秒,最小请求数量为5,失败阈值比例为0.5,熔断时长为30s

    提示:为了触发异常统计,需要修改业务,引发异常,进入兜底方法

    熔断 

     容错类中拿到具体的错误,需要自定义异常类

    默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方。如果要自定义异常时的返回结果,需要实现BlockExceptionHandler接口:

    而BlockException包含很多个子类,分别对应不同的场景:

    自定义异常结果 

    1. package com.zsy.order.hander;
    2. import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
    3. import com.alibaba.csp.sentinel.slots.block.BlockException;
    4. import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
    5. import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
    6. import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
    7. import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
    8. import org.springframework.stereotype.Component;
    9. import javax.servlet.http.HttpServletRequest;
    10. import javax.servlet.http.HttpServletResponse;
    11. @Component
    12. public class SentinelBlockHandler implements BlockExceptionHandler {
    13. @Override
    14. public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
    15. String msg = "未知异常";
    16. int status = 429;
    17. if (e instanceof FlowException) {
    18. msg = "请求被限流了!";
    19. }else if (e instanceof DegradeException) {
    20. msg = "请求被降级了!";
    21. } else if (e instanceof ParamFlowException) {
    22. msg = "热点参数限流!";
    23. } else if (e instanceof AuthorityException) {
    24. msg = "请求没有权限!";
    25. status = 401;
    26. }
    27. httpServletResponse.setContentType("application/json;charset=utf-8");
    28. httpServletResponse.setStatus(status);
    29. httpServletResponse.getWriter().println("{\"message\": \"" + msg + "\", \"status\": " + status + "}");
    30. }
    31. }

    3.Sentinel规则持久化

    Sentinel的控制台规则管理有三种模式

    原始模式:控制台配置的规则直接推送到Sentinel客户端,也就是我们的应用。然后保存在内存中,服务重启则丢失

    pull模式:控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库中查询,更新本地规则。 

    1 编写处理类

    1. package com.zsy.order.config;
    2. import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
    3. import com.alibaba.csp.sentinel.datasource.*;
    4. import com.alibaba.csp.sentinel.init.InitFunc;
    5. import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
    6. import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
    7. import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
    8. import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
    9. import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
    10. import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
    11. import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
    12. import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
    13. import com.alibaba.csp.sentinel.slots.system.SystemRule;
    14. import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
    15. import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
    16. import com.alibaba.fastjson.JSON;
    17. import com.alibaba.fastjson.TypeReference;
    18. import org.springframework.beans.factory.annotation.Value;
    19. import java.io.File;
    20. import java.io.IOException;
    21. import java.util.List;
    22. public class FilePersistence implements InitFunc {
    23. @Value("${spring.application.name}")
    24. private String appcationName;
    25. @Override
    26. public void init() throws Exception {
    27. String ruleDir = "./sentinel-rules/" + appcationName;
    28. String flowRulePath = ruleDir + "/flow-rule.json";
    29. String degradeRulePath = ruleDir + "/degrade-rule.json";
    30. String systemRulePath = ruleDir + "/system-rule.json";
    31. String authorityRulePath = ruleDir + "/authority-rule.json";
    32. String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
    33. this.mkdirIfNotExits(ruleDir);
    34. this.createFileIfNotExits(flowRulePath);
    35. this.createFileIfNotExits(degradeRulePath);
    36. this.createFileIfNotExits(systemRulePath);
    37. this.createFileIfNotExits(authorityRulePath);
    38. this.createFileIfNotExits(paramFlowRulePath);
    39. // 流控规则
    40. ReadableDataSource> flowRuleRDS = new FileRefreshableDataSource<>(
    41. flowRulePath,
    42. flowRuleListParser
    43. );
    44. FlowRuleManager.register2Property(flowRuleRDS.getProperty());
    45. WritableDataSource> flowRuleWDS = new FileWritableDataSource<>(
    46. flowRulePath,
    47. this::encodeJson
    48. );
    49. WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
    50. // 降级规则
    51. ReadableDataSource> degradeRuleRDS = new FileRefreshableDataSource<>(
    52. degradeRulePath,
    53. degradeRuleListParser
    54. );
    55. DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
    56. WritableDataSource> degradeRuleWDS = new FileWritableDataSource<>(
    57. degradeRulePath,
    58. this::encodeJson
    59. );
    60. WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
    61. // 系统规则
    62. ReadableDataSource> systemRuleRDS = new FileRefreshableDataSource<>(
    63. systemRulePath,
    64. systemRuleListParser
    65. );
    66. SystemRuleManager.register2Property(systemRuleRDS.getProperty());
    67. WritableDataSource> systemRuleWDS = new FileWritableDataSource<>(
    68. systemRulePath,
    69. this::encodeJson
    70. );
    71. WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
    72. // 授权规则
    73. ReadableDataSource> authorityRuleRDS = new FileRefreshableDataSource<>(
    74. authorityRulePath,
    75. authorityRuleListParser
    76. );
    77. AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
    78. WritableDataSource> authorityRuleWDS = new FileWritableDataSource<>(
    79. authorityRulePath,
    80. this::encodeJson
    81. );
    82. WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
    83. // 热点参数规则
    84. ReadableDataSource> paramFlowRuleRDS = new FileRefreshableDataSource<>(
    85. paramFlowRulePath,
    86. paramFlowRuleListParser
    87. );
    88. ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
    89. WritableDataSource> paramFlowRuleWDS = new FileWritableDataSource<>(
    90. paramFlowRulePath,
    91. this::encodeJson
    92. );
    93. ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    94. }
    95. private Converter> flowRuleListParser = source -> JSON.parseObject(
    96. source,
    97. new TypeReference>() {
    98. }
    99. );
    100. private Converter> degradeRuleListParser = source -> JSON.parseObject(
    101. source,
    102. new TypeReference>() {
    103. }
    104. );
    105. private Converter> systemRuleListParser = source -> JSON.parseObject(
    106. source,
    107. new TypeReference>() {
    108. }
    109. );
    110. private Converter> authorityRuleListParser = source -> JSON.parseObject(
    111. source,
    112. new TypeReference>() {
    113. }
    114. );
    115. private Converter> paramFlowRuleListParser = source -> JSON.parseObject(
    116. source,
    117. new TypeReference>() {
    118. }
    119. );
    120. private void mkdirIfNotExits(String filePath) throws IOException {
    121. File file = new File(filePath);
    122. if (!file.exists()) {
    123. file.mkdirs();
    124. }
    125. }
    126. private void createFileIfNotExits(String filePath) throws IOException {
    127. File file = new File(filePath);
    128. if (!file.exists()) {
    129. file.createNewFile();
    130. }
    131. }
    132. private String encodeJson(T t) {
    133. return JSON.toJSONString(t);
    134. }
    135. }

     2 添加配置

    resources下创建配置目录 META-INF/services ,然后添加文件

    com.alibaba.csp.sentinel.init.InitFunc

    在文件中添加配置类的全路径

    com.zsy.order.config.FilePersistence

    push模式:控制台将配置规则推送到远程配置中心,例如Nacos。Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。

  • 相关阅读:
    Linux常见基本指令合集及其效果展示
    晕,考试的几何画板迭代问题
    云原生安全——docker逃逸
    python入门
    11.10作业
    SpringBoot学习之自定义注解(二十五)
    【lombok原理】无聊的周末一个人手写一个lombok
    【Android】 Glide 官方库自带的图片裁切类
    基于神经网络的手势识别,人工神经网络模式识别
    响应格式规范
  • 原文地址:https://blog.csdn.net/qq_44189274/article/details/126530938