下述Sentinel规则都使用自定义的异常类 MyBlockExceptionHandler 实现 BlockExceptionHandler
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.company.model.RestObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 自定义限流降级错误信息
*/
@Slf4j
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
log.info("UrlBlockHandler.....................................");
RestObject restObject = null;
// 不同的异常返回不同的提示语
if (e instanceof FlowException) {
restObject = RestObject.builder().statusCode(100).statusMessage("接口限流了").build();
} else if (e instanceof DegradeException) {
restObject = RestObject.builder().statusCode(101).statusMessage("服务降级了").build();
} else if (e instanceof ParamFlowException) {
restObject = RestObject.builder().statusCode(102).statusMessage("热点参数限流了").build();
} else if (e instanceof SystemBlockException) {
restObject = RestObject.builder().statusCode(103).statusMessage("触发系统保护规则").build();
} else if (e instanceof AuthorityException) {
restObject = RestObject.builder().statusCode(104).statusMessage("授权规则不通过").build();
}
//返回json数据
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
//springmvc 的一个json转换类 (jackson)
new ObjectMapper().writeValue(response.getWriter(), restObject);
//跳转
//request.getRequestDispatcher("/index.jsp").forward(request, response);
//重定向
//response.sendRedirect("http://www.baidu.com");
}
}
没有自定义就会走默认的实现类 DefaultBlockExceptionHandler
一、Sentinel 流控规则
资源名:一般是我们的请求路径
针对来源:来自哪个应用
阈值类型:分为QPS或线程数
单机阈值:单个节点的QPS或线程数阈值
是否集群:被请求的服务是否集群部署
流控模式:
(1)直接,就是直接对该资源进行控制
(2)关联,关联某一个资源(/app2),被关联的资源/app2达到阈值2,则限制当前资源/test的访问
(3)链路,记录指定链路上的流量
流控效果:
(1)快速失败,直接限制;
(2)Warm Up,根据coldFactor(默认为3)的值,从 阈值/coldFactor,经过预热的时长,才达到设置的QPS阈值,比如设置QPS阈值为100,那么100/3 =33,用33作为最初的阈值,然后在10秒到达100后再开始限流;
(3)排队等待,在QPS阈值到达后,新的请求就等待,直到超时,可以适用于突发流量的请求
如,设置阈值类型 QPS,单机阈值为2,快速刷新浏览器
上述报错提示都可以在 sentinel 项目的源码中找到
https://github.com/alibaba/Sentinel
二、 Sentinel 熔断/降级规则
Sentinel 1.8.0及以上版本对熔断特性进行了全新的改进升级
1、慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断
2、异常比例:异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO)是指当资源的每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回,异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%;
3、异常数:异常数 (DEGRADE_GRADE_EXCEPTION_COUNT)是指当资源近1分钟的异常数目超过阈值之后会进行熔断,注意由于统计时间窗口是分钟级别的,若timeWindow小于 60s,则结束熔断状态后仍可能再进入熔断状态
Sentinel 1.8.0之前版本叫 降级规则
在1.8之前的版本慢调用比例就是RT,慢调用相较于之前的RT加入了比例阈值,相当于多加了一个条件
降级策略:
(1)RT:平均响应时间 (DEGRADE_GRADE_RT),当 1s 内持续进入 N 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException),注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置
在浏览器访问http://localhost:8081/test
,连续快速刷新该请求,可以看到服务被熔断。1s后自动恢复
三、Sentinel 热点规则
热点即经常访问的数据,统计某个热点数据中访问频次最高的数据,并对其访问进行限制,如:
商品ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流,热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效
注:
1、 热点规则需要使用@SentinelResource("app")注解,否则不生效;
/**
* blockHandler = "block", blockHandlerClass = MyBlockHandlerClass.class 处理限流和降级
* fallback = "fallback", fallbackClass = MyFallbackClass.class 处理限流和降级
*/
@GetMapping("/app") // 埋点:加入sentinel的监控
//@SentinelResource(value = "app", fallback = "fallback", fallbackClass = MyFallbackClass.class)
@SentinelResource(value = "app", blockHandler = "block", blockHandlerClass = MyBlockHandlerClass.class)
public String app(@RequestParam(value = "a", required = false) String a,
@RequestParam(value = "b", required = false) String b) {
System.out.println("/app/--> " + a + "--" + b);
return restTemplate.getForObject("http://springcloud-alibaba-1-nacos-discovery-provider/test", String.class);
}
MyFallbackClass
public class MyFallbackClass {
/**
* 该方法一定要是static方法
*/
public static String fallback(String a, String b) {
System.out.println("Fall back--> " + a + "--" + b);
return "Fall back.";
}
public static String fallback2(String a, String b) {
System.out.println("Fall back 2--> " + a + "--" + b);
return "Fall back 2.";
}
}
MyBlockHandlerClass
public class MyBlockHandlerClass {
/**
* 该方法一定要是static方法
*/
public static String block(String a, String b, BlockException e) {
System.out.println("Block Handler--> " + a + "--" + b);
return "Block Handler.";
}
/**
* 限流后处理方法
*/
public static SentinelClientHttpResponse blockA(HttpRequest request,
byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
System.err.println("block: " + ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse("custom block info");
}
/**
* 熔断后处理的方法
*/
public static SentinelClientHttpResponse fallbackA(HttpRequest request,
byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
System.err.println("fallback: " + ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse("custom fallback info");
}
}
2、 参数必须是7种基本数据类型才会生效;
@SentinelResource 注解
属性 | 说明 | 必填与否 | 使用要求 |
---|---|---|---|
value | 用于指定资源的名称 | 必填 | - |
entryType | entry 类型 | 可选项(默认为 EntryType.OUT) | - |
blockHandler | 服务限流后会抛出 BlockException 异常,而 blockHandler 则是用来指定一个函数来处理 BlockException 异常的。 简单点说,该属性用于指定服务限流后的后续处理逻辑。 |
可选项 |
|
blockHandlerClass | 若 blockHandler 函数与原方法不在同一个类中,则需要使用该属性指定 blockHandler 函数所在的类。 | 可选项 |
|
fallback | 用于在抛出异常(包括 BlockException)时,提供 fallback 处理逻辑。 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。 |
可选项 |
|
fallbackClass | 若 fallback 函数与原方法不在同一个类中,则需要使用该属性指定 blockHandler 函数所在的类。 | 可选项 |
|
defaultFallback | 默认的 fallback 函数名称,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。 默认 fallback 函数可以针对所以类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。 |
可选项 |
|
exceptionsToIgnore | 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。 | 可选项 | - |
四、Sentinel 系统规则
**Load:**Load自适应(仅对Linux/Unix-like机器生效):系统的load1作为启发指标,进行自适应系统保护,当系统load1超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护,系统容量由系统的maxQps * minRt估算得出,设定参考值一般是 CPU cores * 2.5
平均 RT:当单台机器上所有入口流量的平均RT达到阈值即触发系统保护,单位是毫秒
**并发线程数:**当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护
**入口 QPS:**当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护
CPU使用率 usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏
五、Sentinel授权规则
实现 RequestOriginParser 接口,在接口方法中实现区分来源
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class MyRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
String origin = request.getParameter("origin");
// if (StringUtils.isBlank(origin)) {
// throw new IllegalArgumentException("origin参数未指定");
// }
return origin;
}
}
浏览器输入快速访问 http://localhost:8081/app?origin=order