雪崩效应

解决方案

1、设置线程超时

2、设置限流

3、熔断器 Sentinel、Hystrix

1、pom.xml 引入依赖

  1. <dependency>
  2. <groupId>com.alibaba.cloud</groupId>
  3. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-actuator</artifactId>
  8. </dependency>

2、application 配置

  1. # actuator暴露所有端点
  2. management:
  3. endpoints:
  4. web:
  5. exposure:
  6. include: '*'
  7. # 与Sentinel DashBoard交互地址
  8. spring:
  9. cloud:
  10. sentinel:
  11. transport:
  12. dashboard: localhost:8080

3、下载 Sentinel 控制台:https://github.com/alibaba/Sentinel/releases,启动:`java -jar sentinel-dashboard-1.8.0.jar` 账号密码皆为sentinel,启动nacos,启动provider模块和consumer模块,访问http://localhost:9090/index

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图1

1、流控规则

直接限流:直接对关联的url资源限流

开启sentinel,开启provider服务

对/index资源做流控,设置QPS为1(表示一秒钟只允许访问一次)

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图2

当访问一秒钟访问http://localhost:8001/index超过一次时,会限制显示被流控限制阻塞

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图3

关联限流:当被访问的url资源超过设定的阈值,限流关联的资源

controller新增list方法:

  1. @GetMapping("/list")
  2. public String list() {
  3. return "list";
  4. }

添加测试依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-test</artifactId>
  4. <scope>test</scope>
  5. <exclusions>
  6. <exclusion>
  7. <groupId>org.junit.vintage</groupId>
  8. <artifactId>junit-vintage-engine</artifactId>
  9. </exclusion>
  10. </exclusions>
  11. </dependency>

需要同时范文资源才能看到效果,测试类对http://localhost:8001/index访问

  1. @Test
  2. @DisplayName("测试关联流控模式")
  3. void test1() throws InterruptedException {
  4. RestTemplate restTemplate = new RestTemplate();
  5. for (int i = 0; i < 100; ++i) {
  6. restTemplate.getForObject("http://localhost:8081/index", String.class);
  7. System.out.println("provider==>/index=======>" + i);
  8. //休眠200毫秒
  9. TimeUnit.MILLISECONDS.sleep(200);
  10. }
  11. }

启动provider程序,设置流控规则如下:

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图4

设置完流控规则后启动测试程序,浏览器访问http://localhost:8001/list则出现以下情况,表示对index访问超过阈值,则关联资源list限流

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图5

链路限流:对更深层次资源限流(不仅仅局限于controller)

1、pom.xml 添加依赖

  1. <dependency>
  2. <groupId>com.alibaba.csp</groupId>
  3. <artifactId>sentinel-web-servlet</artifactId>
  4. </dependency>

2、application.yml

  1. spring:
  2. cloud:
  3. sentinel:
  4. filter:
  5. enabled: false

3、写配置类

  1. package com.godfrey.configuration;
  2. import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
  3. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import javax.servlet.Filter;
  7. /**
  8. * @author godfrey
  9. * @since 2020-12-07
  10. */
  11. @Configuration
  12. public class FilterConfiguration {
  13. @Bean
  14. public FilterRegistrationBean<Filter> registrationBean() {
  15. FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
  16. registrationBean.setFilter(new CommonFilter());
  17. registrationBean.addUrlPatterns("/*");
  18. registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
  19. registrationBean.setName("sentinelFilter");
  20. return registrationBean;
  21. }
  22. }

4、Service

  1. @Service
  2. public class ProviderService {
  3. @SentinelResource("test") //test 保护资源名
  4. public void test() {
  5. System.out.println("test");
  6. }
  7. }

5、Controller

  1. private final ProviderService providerService;
  2. @Autowired
  3. public ProviderController(ProviderService providerService) {
  4. this.providerService = providerService;
  5. }
  6. @GetMapping("/test1")
  7. public String test1() {
  8. this.providerService.test();
  9. return "test1";
  10. }
  11. @GetMapping("/test2")
  12. public String test2() {
  13. this.providerService.test();
  14. return "test2";
  15. }

为了对比,test1做链路限流,对test2不做限流,设置如下

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图6

一秒钟访问http://localhost:8081/test1超过一次时,会对service绑定资源限流

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图7

访问http://localhost:8081/test2则不会

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图8

2、流控效果

快速失败

直接抛出异常

Warm UP

给系统一个预热的时间,预热时间段内单机阈值较低,预热时间过后单机阈值增加,预热时间内当前的单机阈值是设置的阈值的三分之一,预热时间过后单机阈值恢复设置的值。

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图9

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图10

排队等待

当请求调用失败之后,不会立即抛出异常,等待下一次调用,时间范围是超时时间,在时间范围内如果请求则抛出异常。阀值类型必须设成QPS,否则无效

3、降级规则

RT

单个请求的响应时间超过阈值,则进入准降级状态,接下来 1 S 内连续 5 个请求响应时间均超过阈值,就进行降级,持续时间为时间窗口的值。

异常比例

每秒异常数量占通过量的比例大于阈值,就进行降级处理,持续时间为时间窗口的值。

异常数

1 分钟内的异常数超过阈值就进行降级处理,时间窗口的值要大于 60S,否则刚结束熔断又进入下一次熔断了。

4、热点规则

热点规则是流控规则的更细粒度操作,可以具体到对某个热点参数的限流,设置限流之后,如果带着限流参数的请求量超过阈值,则进行限流,时间为统计窗口时长。

必须要添加 @SentinelResource,即对资源进行流控。

  1. @GetMapping("/hot")
  2. @SentinelResource("hot")
  3. public String hot(
  4. @RequestParam(value = "num1", required = false) Integer num1,
  5. @RequestParam(value = "num2", required = false) Integer num2) {
  6. return num1 + "-" + num2;
  7. }

对参数num1进行限流Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图11

效果:

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图12

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图13

可以添加例外值:当传的对应参数值等于例外值的时候,读取的阈值为例外设定阈值

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图14

5、授权规则

给指定的资源设置流控应用(追加参数),可以对流控应用进行访问权限的设置,具体就是添加白名单和黑名单。

如何给请求指定流控应用,通过实现 RequestOriginParser 接口来完成,代码如下所示。

  1. package com.godfrey.configuration;
  2. import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
  3. import org.springframework.util.StringUtils;
  4. import javax.servlet.http.HttpServletRequest;
  5. /**
  6. * @author godfrey
  7. * @since 2020-12-26
  8. */
  9. public class RequestOriginParserDefinition implements RequestOriginParser {
  10. @Override
  11. public String parseOrigin(HttpServletRequest httpServletRequest) {
  12. String name = httpServletRequest.getParameter("name");
  13. if (StringUtils.isEmpty(name)) {
  14. throw new RuntimeException("name is null");
  15. }
  16. return name;
  17. }
  18. }

要让 RequestOriginParserDefinition 生效,需要在配置类中进行配置。

  1. package com.godfrey.configuration;
  2. import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
  3. import org.springframework.context.annotation.Configuration;
  4. import javax.annotation.PostConstruct;
  5. /**
  6. * @author godfrey
  7. * @since 2020-12-26
  8. */
  9. @Configuration
  10. public class SentinelConfiguration {
  11. @PostConstruct
  12. public void init() {
  13. WebCallbackManager.setRequestOriginParser(new RequestOriginParserDefinition());
  14. }
  15. }

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图15

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图16

Spring Cloud Alibaba 04:Sentinel 服务限流降级 - 图17

6 自定义规则异常返回

创建异常处理类

  1. package com.godfrey.execption;
  2. import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
  3. import com.alibaba.csp.sentinel.slots.block.BlockException;
  4. import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
  5. import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import java.io.IOException;
  9. /**
  10. * @author godfrey
  11. * @since 2020-12-26
  12. */
  13. public class ExceptionHandler implements UrlBlockHandler {
  14. @Override
  15. public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
  16. httpServletResponse.setContentType("text/html;charset=utf-8");
  17. String msg = null;
  18. if (e instanceof FlowException) {
  19. msg = "限流";
  20. } else if (e instanceof DegradeException) {
  21. msg = "降级";
  22. }
  23. httpServletResponse.getWriter().write(msg);
  24. }
  25. }

进行配置。

  1. @Configuration
  2. public class SentinelConfiguration {
  3. @PostConstruct
  4. public void init2() {
  5. WebCallbackManager.setUrlBlockHandler(new ExceptionHandler());
  6. }
  7. }