雪崩效应
解决方案
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
*/
@Configuration
public class FilterConfiguration {
@Bean
public 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
@Service
public class ProviderService {
@SentinelResource("test") //test 保护资源名
public void test() {
System.out.println("test");
}
}
5、Controller
private final ProviderService providerService;
@Autowired
public 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 {
@Override
public 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
*/
@Configuration
public class SentinelConfiguration {
@PostConstruct
public 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 {
@Override
public 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);
}
}
进行配置。
@Configuration
public class SentinelConfiguration {
@PostConstruct
public void init2() {
WebCallbackManager.setUrlBlockHandler(new ExceptionHandler());
}
}