sentinel是面向分布式服务框架的轻量级流量控制框架,主要以流量为切入点,从流量控制,熔断降级,系统负载保护等多个维度来维护系统的稳定性。
资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
针对来源
Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个微服务进行限流 ,默认default(不区分来源,全部限制)
阈值类型
流控模式
使用簇点链路时可能需要展开链路关系,基本不用
/**
* 在spring-cloud-alibaba v2.1.1.RELEASE及前,sentinel1.7.0及后,关闭URL PATH聚合需要通过该方式
* 在spring-cloud-alibaba v2.1.1.RELEASE后,可以配置关闭:spring.cloud.sentinel.web-context-unify=false
* 手动注入Sentinel的过滤器,关闭Sentinel注入CommonFilter实例,
* 修改配置文件中的 spring.cloud.sentinel.filter.enabled=false
*/
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/*");
// 入口资源关闭聚合
registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration;
}
链路必须使用
@SentinelResource
实现监控
流控效果
spring.cloud.sentinel.flow.coldFactor
设置冷加载因子】 热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流,热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效
降级策略
-Dcsp.sentinel.statistic.max.rt=4900
来配置[0.0, 1.0]
,代表0% - 100%
Sentinel系统自适应保护从整体维度对应用入口流量进行控制,结合应用的Load、总体平均RT、入口QPS和线程数等几个维度的监控指标,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
Load:仅对Linux/Unix-like机器生效,系统的load1【uptime
命令】作为启发指标,进行自适应系统保护,当系统load1超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护,系统容量由系统的maxQps * minRt估算得出,设定参考值一般是CPU cores * 2.5
平均RT:当单台机器上所有入口流量的平均RT达到阈值即触发系统保护,单位是毫秒
并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护
入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护
CPU使用率:当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏
当我们需要根据调用来源来判断该次请求是否允许放行,这时候可以就使用Sentinel的来源访问控制的功能,对应的操作就是加上相应的授权规则
/**
* 解析流控应用表示
*/
@Component
public class CustomRequestOriginParser implements RequestOriginParser {
// 获取调用方标识信息并返回
@Override
public String parseOrigin(HttpServletRequest request) {
String serviceName = request.getParameter("serviceName");
StringBuffer url = request.getRequestURL();
if (url.toString().endsWith("favicon.ico")) {
// 浏览器会向后台请求favicon.ico图标
return serviceName;
}
if (StringUtils.isEmpty(serviceName)) {
throw new IllegalArgumentException("serviceName must not be null");
}
return serviceName;
}
}
/**
* 拉模式规则持久化
*/
public class FileDataSourceInit implements InitFunc {
@Override
public void init() throws Exception {
// 存放路径
String ruleDir = System.getProperty("user.home") + "/sentinel/rules";
String flowRulePath = ruleDir + "/flow-rule.json";
String degradeRulePath = ruleDir + "/degrade-rule.json";
String systemRulePath = ruleDir + "/system-rule.json";
String authorityRulePath = ruleDir + "/authority-rule.json";
String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
this.mkdirIfNotExits(ruleDir);
this.createFileIfNotExits(flowRulePath);
this.createFileIfNotExits(degradeRulePath);
this.createFileIfNotExits(systemRulePath);
this.createFileIfNotExits(authorityRulePath);
this.createFileIfNotExits(paramFlowRulePath);
// 流控规则
ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
flowRulePath,
flowRuleListParser
);
// 将可读数据源注册至FlowRuleManager
// 这样当规则文件发生变化时,就会更新规则到内存
FlowRuleManager.register2Property(flowRuleRDS.getProperty());
WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
flowRulePath,
this::encodeJson
);
// 将可写数据源注册至transport模块的WritableDataSourceRegistry中
// 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
// 降级规则
ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
degradeRulePath,
degradeRuleListParser
);
DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
degradeRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
// 系统规则
ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
systemRulePath,
systemRuleListParser
);
SystemRuleManager.register2Property(systemRuleRDS.getProperty());
WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
systemRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
// 授权规则
ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
flowRulePath,
authorityRuleListParser
);
AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
authorityRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
// 热点参数规则
ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
paramFlowRulePath,
paramFlowRuleListParser
);
ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
paramFlowRulePath,
this::encodeJson
);
ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
}
private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<FlowRule>>() {
}
);
private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<DegradeRule>>() {
}
);
private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<SystemRule>>() {
}
);
private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<AuthorityRule>>() {
}
);
private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<ParamFlowRule>>() {
}
);
private void mkdirIfNotExits(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
}
private void createFileIfNotExits(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
}
private <T> String encodeJson(T t) {
return JSON.toJSONString(t);
}
}
在resources下创建配置目录META-INF/services
,然后添加文件com.alibaba.csp.sentinel.init.InitFunc
在文件中添加配置类全类名
引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
配置yaml
spring:
cloud:
sentinel:
datasource:
ds:
nacos:
serverAddr: localhost:8848
groupId: WYK_GROUP
namespace: DEV
dataId: ${spring.application.name}-sentinel
dataType: 'json'
ruleType: FLOW
创建配置文件${spring.application.name}-sentinel
[
{
"clusterConfig": {
"fallbackToLocalWhenFail": true,
"sampleCount": 10,
"strategy": 0,
"thresholdType": 0,
"windowIntervalMs": 1000
},
"clusterMode": false,
"controlBehavior": 0,
"count": 10.0,
"grade": 1,
"limitApp": "default",
"maxQueueingTimeMs": 500,
"resource": "/payment/{id}",
"strategy": 0,
"warmUpPeriodSec": 10
}
]
/**
* 自定义sentinel报错信息
*/
@Component
public class CustomBlockExceptionHandler implements BlockExceptionHandler {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(CharEncoding.UTF_8);
PrintWriter writer = response.getWriter();
// 触发限流
if(e instanceof FlowException) {
response.setStatus(2001);
writer.print(objectMapper.writeValueAsString(CommentResult.failed("触发限流")));
}
// 授权规则不通过
else if(e instanceof AuthorityException) {
response.setStatus(2002);
writer.print(objectMapper.writeValueAsString(CommentResult.failed("授权规则不通过")));
}
// 服务降级
else if(e instanceof DegradeException) {
response.setStatus(2003);
writer.print(objectMapper.writeValueAsString(CommentResult.failed("服务降级")));
}
// 热点参数限流
else if(e instanceof ParamFlowException) {
response.setStatus(2004);
writer.print(objectMapper.writeValueAsString(CommentResult.failed("触发热点限流")));
}
// 系统保护
else if(e instanceof SystemBlockException) {
response.setStatus(2005);
writer.print(objectMapper.writeValueAsString(CommentResult.failed("触发系统保护规则")));
}
// 兜底
else {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
writer.print(objectMapper.writeValueAsString(CommentResult.failed("发生未知错误")));
}
writer.flush();
writer.close();
}
}
@SentinelResource注解还有blockHandler
、blockHandlerClass
、fallback
、fallbackClass
用于限流和服务降级使用方法类似HystrixCommand
注解
OpenFeign也可以和Sentinel配合使用@FeignClient
中fallback
来实现降级