雪崩效应
解决方案
1、设置线程超时
2、设置限流
3、熔断器 Sentinel、Hystrix
1、pom.xml 引入依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
2、application 配置
# actuator暴露所有端点management:endpoints:web:exposure:include: '*'# 与Sentinel DashBoard交互地址spring:cloud:sentinel:transport: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

1、流控规则
直接限流:直接对关联的url资源限流
开启sentinel,开启provider服务
对/index资源做流控,设置QPS为1(表示一秒钟只允许访问一次)

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

关联限流:当被访问的url资源超过设定的阈值,限流关联的资源
controller新增list方法:
@GetMapping("/list")public String list() {return "list";}
添加测试依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency>
需要同时范文资源才能看到效果,测试类对http://localhost:8001/index访问
@Test@DisplayName("测试关联流控模式")void test1() throws InterruptedException {RestTemplate restTemplate = new RestTemplate();for (int i = 0; i < 100; ++i) {restTemplate.getForObject("http://localhost:8081/index", String.class);System.out.println("provider==>/index=======>" + i);//休眠200毫秒TimeUnit.MILLISECONDS.sleep(200);}}
启动provider程序,设置流控规则如下:

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

链路限流:对更深层次资源限流(不仅仅局限于controller)
1、pom.xml 添加依赖
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-web-servlet</artifactId></dependency>
2、application.yml
spring:cloud:sentinel:filter:enabled: false
3、写配置类
package com.godfrey.configuration;import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import javax.servlet.Filter;/*** @author godfrey* @since 2020-12-07*/@Configurationpublic class FilterConfiguration {@Beanpublic FilterRegistrationBean<Filter> registrationBean() {FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(new CommonFilter());registrationBean.addUrlPatterns("/*");registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");registrationBean.setName("sentinelFilter");return registrationBean;}}
4、Service
@Servicepublic class ProviderService {@SentinelResource("test") //test 保护资源名public void test() {System.out.println("test");}}
5、Controller
private final ProviderService providerService;@Autowiredpublic ProviderController(ProviderService providerService) {this.providerService = providerService;}@GetMapping("/test1")public String test1() {this.providerService.test();return "test1";}@GetMapping("/test2")public String test2() {this.providerService.test();return "test2";}
为了对比,test1做链路限流,对test2不做限流,设置如下

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

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

2、流控效果
快速失败
直接抛出异常
Warm UP
给系统一个预热的时间,预热时间段内单机阈值较低,预热时间过后单机阈值增加,预热时间内当前的单机阈值是设置的阈值的三分之一,预热时间过后单机阈值恢复设置的值。


排队等待
当请求调用失败之后,不会立即抛出异常,等待下一次调用,时间范围是超时时间,在时间范围内如果请求则抛出异常。阀值类型必须设成QPS,否则无效
3、降级规则
RT
单个请求的响应时间超过阈值,则进入准降级状态,接下来 1 S 内连续 5 个请求响应时间均超过阈值,就进行降级,持续时间为时间窗口的值。
异常比例
每秒异常数量占通过量的比例大于阈值,就进行降级处理,持续时间为时间窗口的值。
异常数
1 分钟内的异常数超过阈值就进行降级处理,时间窗口的值要大于 60S,否则刚结束熔断又进入下一次熔断了。
4、热点规则
热点规则是流控规则的更细粒度操作,可以具体到对某个热点参数的限流,设置限流之后,如果带着限流参数的请求量超过阈值,则进行限流,时间为统计窗口时长。
必须要添加 @SentinelResource,即对资源进行流控。
@GetMapping("/hot")@SentinelResource("hot")public String hot(@RequestParam(value = "num1", required = false) Integer num1,@RequestParam(value = "num2", required = false) Integer num2) {return num1 + "-" + num2;}
对参数num1进行限流
效果:


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

5、授权规则
给指定的资源设置流控应用(追加参数),可以对流控应用进行访问权限的设置,具体就是添加白名单和黑名单。
如何给请求指定流控应用,通过实现 RequestOriginParser 接口来完成,代码如下所示。
package com.godfrey.configuration;import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;import org.springframework.util.StringUtils;import javax.servlet.http.HttpServletRequest;/*** @author godfrey* @since 2020-12-26*/public class RequestOriginParserDefinition implements RequestOriginParser {@Overridepublic String parseOrigin(HttpServletRequest httpServletRequest) {String name = httpServletRequest.getParameter("name");if (StringUtils.isEmpty(name)) {throw new RuntimeException("name is null");}return name;}}
要让 RequestOriginParserDefinition 生效,需要在配置类中进行配置。
package com.godfrey.configuration;import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;/*** @author godfrey* @since 2020-12-26*/@Configurationpublic class SentinelConfiguration {@PostConstructpublic void init() {WebCallbackManager.setRequestOriginParser(new RequestOriginParserDefinition());}}



6 自定义规则异常返回
创建异常处理类
package com.godfrey.execption;import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;import com.alibaba.csp.sentinel.slots.block.BlockException;import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;import com.alibaba.csp.sentinel.slots.block.flow.FlowException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** @author godfrey* @since 2020-12-26*/public class ExceptionHandler implements UrlBlockHandler {@Overridepublic void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {httpServletResponse.setContentType("text/html;charset=utf-8");String msg = null;if (e instanceof FlowException) {msg = "限流";} else if (e instanceof DegradeException) {msg = "降级";}httpServletResponse.getWriter().write(msg);}}
进行配置。
@Configurationpublic class SentinelConfiguration {@PostConstructpublic void init2() {WebCallbackManager.setUrlBlockHandler(new ExceptionHandler());}}
