雪崩效应
在下图的微服务中,当A挂掉之后,B还是一直在请求A,那么B发往A的请求就会强制等待直到超时。一个请求就是一个线程,当被等待时候就是线程阻塞,只有当超时之后才会释放;当高并发的情况下多个线程开启就对应了服务器的资源,如果不做处理B也会挂了,同理CD也是如此。
基础服务故障导致上层服务故障,故障不断放大的过程称为雪崩效应,也称为级联失效,级联故障(cascading failure)。
常见容错方案
超时
给每个请求都设置一个比较短的超时时间,比如1秒,不管有没有请求成功,这个线程在1s之后就会被释放。
只要我速度够快,就不容易挂。
限流
如某个微服务最大的QPS是1000,那么就为他设置一个QPS最大值,如800,当有请求进来QPS超过了800,后续的请求就直接拒绝。
最大的量就是设置的值,再多也没用。
仓壁模式
比如两个A、B Controller,每个Controller都有自己的线程池,比如都是20,当线程池满了之后会排队,然后拒绝请求,那么A、B都是自己的线程池,并不会影响对方。
不把鸡蛋都放在同一个篮子里
断路器模式
断路器的思想就是可以为某个服务(接口)去设置一个错误率的阀值,当在一定时间内达到了这个阀值就跳闸(断开),不再去调用,也就是不能访问了。断路器断开之后,过一会会进入半开状态,这是一个瞬间太,他会再去调用服务,如果成功就会关闭断路器,反之继续打开。
监控-开关
使用Sentinel实现容错
面向云原生微服务的高可用流控防护组件
GitHub
官网
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置
management:
endpoints:
web:
exposure:
include: '*'
搭建Sentinel控制台
下载地址
根据自己项目中的sentinel版本进行对应下载
下载对应的jar之后就可以直接启动了,访问http://localhost:8080/
spring:
cloud:
sentinel:
transport:
# 指定sentinel控制台地址
dashboard: localhost:8080
启动项目之后在sentinel是看不到东西的,就一个首页,当访问项目的接口之后就可以看到了,这是因为sentinel是懒加载的这是Ribbon一样。
设置Sentinel流控规则
在族点链路中可以查看到访问过的API,可以为他进行流控设置
QPS:1,直接 就是访问一次之后就限流
关联: 当关联的资源达到阀值,就限流自己。
比如:关联了/actuator/sentinel,当/actuator/sentinel被限流,/shares/1就会被限流。
链路: 只记录指定链路上的流量
@Service
@Slf4j
public class TestService {
@SentinelResource("common")
public String common() {
log.info("执行了Common");
return "common";
}
}
@Autowired
private TestService testService;
@GetMapping("test-a")
public String testA() {
this.testService.common();
return "test-a";
}
@GetMapping("test-b")
public String testB() {
this.testService.common();
return "test-b";
}
为common添加流控
添加链路类型的规则
这时候访问一次/test-a接口之后就不会在拿到结果
也就是说对于common这个资源他是只统计了/test-a接口,可以说这是细粒度针对来源到API接口,而上面的这种针对来源是对于微服务的
快速失败: 直接抛异常
Warm Up: 预热。根据codeFactor(默认3)的值,从阀值/codeFactor,经过预热时长,才到达设置的QPS阀值。
比如:我给阀值100,预热10秒,那么就是100/3作为最初的阈值,经过10秒之后才去达到100限流。
适用场景:适合秒杀开始,当秒杀开始后的时候可以让经过的流量缓慢的增加,不能直接一下子这么多,可能会挂掉。
排队等待: 匀速排队,让请求以均匀的速度通过,阀值类型必须设置QPS,否则无效。
以下的设置:每秒只允许一次请求,其他的在后面排队,直到过了超时时间把其他的请求丢弃。
适用场景:应对突发流量的请求,有时候流量多,有时候又空闲,如果希望机器能在空闲的时候处理请求,而不是直接拒绝。
设置Sentinel降级规则
RT
平均响应时间(秒级统计)超出阀值(1)并且在窗口内通过的请求>=5就会触发降级(打开断路器),等时间窗口结束之后,再关闭降级。
注意点
RT默认最大是4900ms,不管给到多大就只能是4900ms ,如果要进行修改最大是可以修改属性-Dcsp.sentinel.statistic.max.rt=xxx
异常比例
QPS>=5 并且异常比例(秒级统计)超过阀值就会触发降级(断路器打开),等待时间窗口结束,再次关闭降级。
异常数
异常数(分钟统计)超过阀值就会触发降级(断路器打开),等待时间窗口结束,再次关闭降级。
注意点
时间窗口<60秒回出现问题
如果一分钟内统计的异常数大于10就开降级,10秒之后恢复。异常数是分钟级别的,那么可能时间窗口结束的10秒还是在同一分钟里,所以还是会处于降级状态。
热点规则
@GetMapping("test-hot")
@SentinelResource("hot")
public String testHot(
@RequestParam(required = false) String a,
@RequestParam(required = false) String b
) {
return a + " " + b;
}
参数索引0就是代表的接口中的第一个参数,1就是代表的第二个参数,单机阀值(QPS)
在时间窗口内,参数索引的QPS达到了阈值就会限流
意思就是当第一个参数值为5的时候,阈值为1000,否则就是1。热点规则就是一个特殊的流控规则。
适用场景:某个参数的QPS特别搞,而又不影响整个API对于其他参数的调用。
注意点
参数必须是基本类型或者String
系统规则
Load
当系统load1(1分钟的load)超过阈值,且并发线程数超过系统容量时触发,建议设置为CPU核心数*2.5.(仅对Linux和Unix-like机器有效)
系统容量 = maxQps * minRt
maxQps :秒级统计出来的最大QPS
minRt:秒级统计出来的最小响应时间
RT
所有入口流量的平均RT达到阈值触发
线程数
所有入口流量的并发线程数达到阈值触发
入口QPS
所有入口流量的QPS达到阈值触发
授权规则
意思是这个API只允许那个微服务调用或者不被允许调用
代码方式配置
@GetMapping("test-add-flow-rule")
public String testHot() {
this.initFlowQpsRule();
return "success";
}
private void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule("/shares/1");
// set limit qps to 20
rule.setCount(20);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setLimitApp("default");
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
Sentinel与控制台通信原理
其实是服务发现,微服务会注册到Sentinel控制台
这里的端口并不是微服务程序的端口,而是和sentinel的通信端口
在控制台中进行各类操作,其实就是调用了API去完成,API的地址可以通过机器列表上的IP+端口+/api来访问查看
还可以通过微服务自身的IP+端口+//actuator/sentinel来查看相关的信息
哪个IP,端口注册到sentinel上,心跳是10秒一次
控制台配置
应用端配置控制台
spring:
cloud:
sentinel:
transport:
# 指定sentinel控制台地址
dashboard: localhost:8080
# 指定和控制台通信IP
# 不配置,就会自动选择一个IP注册
client-ip: 127.0.0.1
# 和控制台通信的端口,默认8719
# 如果不设置,会从8719开始自动扫描,依次+1,直到找到未占用的端口
port: 8719
# 心跳发送周期,默认值为null
# 在SimpleHttpHeartbeatSender会使用默认值10秒
heartbeat-interval-ms: 10000
控制台配置
[1.6]表示自sentinel的1.6版本之后才有
配置项 | 默认值 | 默认值 | 描述 |
---|---|---|---|
server.port | 8080 | - | 指定端口 |
csp.sentinel.dashboard.server | localhost:8080 | - | 指定地址 |
project.name | 8080 | - | 指定程序的名称 |
sentinel.dashboard.auth.username [1.6] | 8080 | - | Dashboard登录账号 |
sentinel.dashboard.auth.password [1.6] | 8080 | - | Dashboard登录密码 |
server.servlet.session.timeout [1.6] | 8080 | - | 登录Session过期时间,配置为7200表示7200秒,配置为60m表示60分钟 |
配置sentinel的账号密码
java -jar Dsentinel.dashboard.auth.username=XXX -Dsentinel.dashboard.auth.password = XXX sentinel-dashboard-1.6.2.jar
Sentinel API
加了下面的配置,可以防止干扰
spring:
cloud:
sentinel:
# 关闭最springMVC端点的保护
filter:
enabled: false
测试代码进行流控
1.添加流控之后,就会被限流
2.如果来源为test-wfw,那么就会被限流
3.这里的IllegalArgumentException异常也需要进程catch一下,不然的话实现不了流控
@GetMapping("/test-sentinel-api")
public String testSentinelAPI(
@RequestParam(required = false) String a) {
String resourceName = "test-sentinel-api";
ContextUtil.enter(resourceName, "test-wfw");
// 定义一个sentinel保护的资源,名称是test-sentinel-api
Entry entry = null;
try {
entry = SphU.entry(resourceName);
// 被保护的业务逻辑
if (StringUtils.isBlank(a)) {
throw new IllegalArgumentException("a不能为空");
}
return a;
}
// 如果被保护的资源被限流或者降级了,就会抛BlockException
catch (BlockException e) {
log.warn("限流,或者降级了", e);
return "限流,或者降级了";
} catch (IllegalArgumentException e2) {
// 统计IllegalArgumentException【发生的次数、发生占比...】
Tracer.trace(e2);
return "参数非法!";
} finally {
if (entry != null) {
// 退出entry
entry.exit();
}
ContextUtil.exit();
}
}
SphU: 最核心的,定义资源,让资源受到监控,保护资源
Tracer: 对我们想要的异常进行统计
ContextUtil: 可以实现调用来源,标记调用
更多可以参考文档
@SentinelResource使用
属性 | 作用 | 是否保修 |
---|---|---|
value | 资源名称 | 是 |
entryType | entry类型,标记流量的方向,取值IN/OUT,默认是OUT | 否 |
blockHandler | 处理BlockException的函数名称。函数要求:1. 必须是 public2.返回类型与原方法一致3. 参数类型需要和原方法相匹配,并在最后加 BlockException 类型的参数。4. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法。 | 否 |
blockHandlerClass | 存放blockHandler的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同blockHandler。 | 否 |
fallback | 用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求:1. 返回类型与原方法一致2. 参数类型需要和原方法相匹配,Sentinel 1.6开始,也可在方法最后加 Throwable 类型的参数。3.默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定fallbackClass里面的方法。 | 否 |
fallbackClass【1.6】 | 存放fallback的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同fallback。 | 否 |
defaultFallback【1.6】 | 用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求:1. 返回类型与原方法一致2. 方法参数列表为空,或者有一个 Throwable 类型的参数。3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定 fallbackClass 里面的方法。 | 否 |
exceptionsToIgnore【1.6】 | 指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。 | 否 |
exceptionsToTrace | 需要trace的异常 | Throwable |
@GetMapping("/test-sentinel-resource")
@SentinelResource(
value = "test-sentinel-api",
blockHandler = "block",
blockHandlerClass = TestControllerBlockHandlerClass.class,
fallback = "fallback"
)
public String testSentinelResource(@RequestParam(required = false) String a) {
if (StringUtils.isBlank(a)) {
throw new IllegalArgumentException("a cannot be blank.");
}
return a;
}
/**
* 1.5 处理降级
* - sentinel 1.6 可以处理Throwable
*
* @param a
* @return
*/
public String fallback(String a) {
return "限流,或者降级了 fallback";
}
@Slf4j
public class TestControllerBlockHandlerClass {
/**
* 处理限流活降级
* @param a
* @param e
* @return
*/
public static String block(String a, BlockException e) {
log.warn("限流,或者降级了 block", e);
return "限流,或者降级了 block";
}
}
RestTemplate 使用sentinel
在初始化RestTemplate加上@SentinelRestTemplate即可
@Bean
@LoadBalanced
@SentinelRestTemplate
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Autowired
private RestTemplate restTemplate;
@GetMapping("/test-rest-template-sentinel/{userId}")
public UserDTO test(@PathVariable Integer userId) {
return this.restTemplate
.getForObject(
"http://user-center/users/{userId}",
UserDTO.class, userId);
}
@SentinelRestTemplate也可以进行自定义的异常处理,他也包含blockHandler,blockHandlerClass,fallback,fallbackClass,用法跟上面的一样。
关闭@SentinelRestTEmplate,关闭了之后在sentinel控制台就看不到他的粗点链路。
resttemplate:
sentinel:
# 关闭@SentinelRestTEmplate注解
enabled: true
Feign整合Sentinel
配置属性就可以了
feign:
# 为feign整合sentinel
sentinel:
enabled: true
在feign的注释中可以加上fallback,这样可以处理限流之后的操作
@FeignClient(name = "user-center",
fallback = UserCenterFeignClientFallback.class)
public interface UserCenterFeignClient {
@GetMapping("/users/{id}")
UserDTO findById(@PathVariable Integer id);
}
这样在返回的数据中,字段值就变成了一个默认用户,使用fallback是不会有异常捕获的
@Component
public class UserCenterFeignClientFallback implements UserCenterFeignClient {
@Override
public UserDTO findById(Integer id) {
UserDTO userDTO = new UserDTO();
userDTO.setWxNickname("一个默认用户");
return userDTO;
}
}
如果要捕获异常就可以使用fallbackFactory ,这里需要注意的是fallback和fallbackFactory 不能同时使用。
@FeignClient(name = "user-center",
fallbackFactory = UserCenterFeignClientFallbackFactory.class)
public interface UserCenterFeignClient {
@GetMapping("/users/{id}")
UserDTO findById(@PathVariable Integer id);
}
@Component
@Slf4j
public class UserCenterFeignClientFallbackFactory implements FallbackFactory<UserCenterFeignClient> {
@Override
public UserCenterFeignClient create(Throwable throwable) {
return new UserCenterFeignClient() {
@Override
public UserDTO findById(Integer id) {
log.warn("远程调用被限流/降级了", throwable);
UserDTO userDTO = new UserDTO();
userDTO.setWxNickname("一个默认用户");
return userDTO;
}
};
}
}
规则持久化
拉模式
定时读取规则的json文件,如果发现有更新就会修改;
接收控制台的规则配置,写入json文件。
public class FileDataSourceInit implements InitFunc {
@Override
public void init() throws Exception {
// TIPS: 如果你对这个路径不喜欢,可修改为你喜欢的路径
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<>(
authorityRulePath,
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) throws IOException {
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
com.alibaba.csp.sentinel.init.InitFunc文件内就是FileDataSourceInit 类的路径
优点:
- 易懂
- 没有多余依赖
缺点:
- 规则由FileRefreshableDataSource定时更新,更新时间有延迟,如果定时时间过长,那么规则更新延迟,反之影响性能;
- 规则文件在本地,如果迁移,需要将文件一起迁移,可能造成丢失。
推模式
将规则推送到远程配置中心
sentinel获取远程配置中心的规则,如果监听到规则有变化,就更新本地规则缓存。
如何配置推模式,工作量比较大,请参考博客
优点:
- 规则持久化
- 性能好
- 数据一致性
缺点:
- 构造复杂
- 引入依赖(服务发现组件)
集群流控
需要引入Token的依赖,微服务还要继承Token Client来实现
官方文档
sentinel错误页优化
sentinel提供了这个错误提示的处理接口 UrlBlockHandler
@Component
public class MyUrBlockHandler implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
ErrorMsg msg = null;
if (e instanceof FlowException) {
msg = ErrorMsg.builder()
.status(100)
.msg("限流了")
.build();
} else if (e instanceof DegradeException) {
msg = ErrorMsg.builder()
.status(101)
.msg("降级了")
.build();
} else if (e instanceof ParamFlowException) {
msg = ErrorMsg.builder()
.status(102)
.msg("降级了")
.build();
} else if (e instanceof SystemBlockException) {
ErrorMsg.builder()
.status(103)
.msg("降级了")
.build();
} else if (e instanceof AuthorityException) {
msg = ErrorMsg.builder()
.status(104)
.msg("降级了")
.build();
}
httpServletResponse.setStatus(500);
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
httpServletResponse.setContentType("application/json;charset=utf-8");
// spring mvc自带的json操作工具,叫jackson
new ObjectMapper()
.writeValue(
httpServletResponse.getWriter(),
msg
);
}
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
class ErrorMsg {
private Integer status;
private String msg;
}
sentinel区分来源
sentinel提供RequestOriginParser接口
@Component
public class MyRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
// 从请求参数中获取 origin的参数并返回
// 如果获取不到origin参数,那么就抛异常
String origin = request.getParameter("origin");
if (StringUtils.isBlank(origin)) {
throw new IllegalArgumentException("orgin must be specified");
}
return origin;
}
}
实际中可以把来源放到header中
sentinel支持RESTful资源
sentinel提供UrlCleaner接口
@Component
public class MyUrlCleaner implements UrlCleaner {
@Override
public String clean(String s) {
// 让share/1和share/2 返回值相同
// 返回 share/{number}
String[] split = s.split("/");
return Arrays.stream(split)
.map(string -> {
if (NumberUtils.isNumber(string)) {
return "{number}";
}
return s;
})
.reduce((a, b) -> a + "/" + b)
.orElse("");
}
}
sentinel配置总结
Spring Cloud Alibaba Sentienl相关配置项
配置项 | 含义 | 默认值 |
---|---|---|
spring.cloud.sentinel.enabled | Sentinel自动化配置是否生效 | true |
spring.cloud.sentinel.eager | 取消Sentinel控制台懒加载 | false |
spring.cloud.sentinel.transport.port | 应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer | 8719 |
spring.cloud.sentinel.transport.dashboard | Sentinel 控制台地址 | |
spring.cloud.sentinel.transport.heartbeat-interval-ms | 应用与Sentinel控制台的心跳间隔时间 | |
spring.cloud.sentinel.transport.client-ip | 客户端IP | |
spring.cloud.sentinel.filter.order | Servlet Filter的加载顺序。Starter内部会构造这个filter | Integer.MIN_VALUE |
spring.cloud.sentinel.filter.url-patterns | 数据类型是数组。表示Servlet Filter的url pattern集合 | /* |
spring.cloud.sentinel.filter.enabled | Enable to instance CommonFilter | true |
spring.cloud.sentinel.metric.charset | metric文件字符集 | UTF-8 |
spring.cloud.sentinel.metric.file-single-size | Sentinel metric 单个文件的大小 | |
spring.cloud.sentinel.metric.file-total-count | Sentinel metric 总文件数量 | |
spring.cloud.sentinel.log.dir | Sentinel 日志文件所在的目录 | |
spring.cloud.sentinel.log.switch-pid | Sentinel 日志文件名是否需要带上pid | |
spring.cloud.sentinel.servlet.block-page | 自定义的跳转 URL,当请求被限流时会自动跳转至设定好的 URL | |
spring.cloud.sentinel.flow.cold-factor | 冷启动因子 | 3 |
详细参见大目老师手记