006-服务容错-Sentinel
# 一、雪崩效应
雪崩效应:基础服务故障导致上层服务故障,并且这个故障不断放大的过程称之为雪崩效应。
如下图,假设这是一个高并发的应用系统。当A服务不可用时,B依然向A发出请求并且请求不会立即失败,而是等待超时,这就造成了线程阻塞,时间久了,B服务也可能挂掉。随着时间推移,C、D服务最终也可能会挂掉。
# 二、常见的容错方案
- 超时
如果每次请求设置一秒的超时时间,B服务请求A服务,超过一秒,线程就被释放。只要线程释放的速度够快,那么服务就不会被A服务拖死了。
- 限流
设置B服务的QBS为一定值,如果到达阈值,就不会继续接受请求。
# 三、Sentinel 分布式系统的流量防卫兵 (opens new window)
# 3.1 在您的项目中引入 Sentinel
如何使用 Spring Cloud Alibaba Sentinel (opens new window)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2
3
4
整合成功后,会暴露出一个 /actuator/sentinel
端点
spring:
cloud:
sentinel:
transport:
#指定 sentinel 控制台地址
dashboard: localhost:8080
2
3
4
5
6
🤯 Endpoint支持 (opens new window)
# 3.2 Sentinel 控制台
在官方仓库release页面下载jar包,https://github.com/alibaba/Sentinel/releases (opens new window)
根据上面👆🏻sentinel的版本下载对应的jar。
🧐启动控制台
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -Dsentinel.dashboard.auth.username=admin -Dsentinel.dashboard.auth.password=123456 -jar sentinel-dashboard.jar
-Dsentinel.dashboard.auth.username=sentinel
用于指定控制台的登录用户名为sentinel
;(1.6.0版本以上)-Dsentinel.dashboard.auth.password=123456
用于指定控制台的登录密码为123456
;(1.6.0版本以上)
更多 Sentinel 控制台的使用及问题参考: Sentinel 控制台文档 (opens new window) 以及 Sentinel FAQ (opens new window)
从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel
。可以参考 鉴权模块文档 (opens new window) 配置用户名和密码。
项目启动时,控制台是看不到的,因为Sentinel是懒加载的。请求一下项目接口才能显示出来。
# 3.3 流控规则
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
FlowSlot
会根据预设的规则,结合 NodeSelectorSlot
、ClusterBuilderSlot
、StatisticSlot
统计出来的实时信息进行流量控制。
限流的直接表现是在执行 Entry nodeA = SphU.entry(resourceName)
的时候抛出 FlowException
异常。FlowException
是 BlockException
的子类,您可以捕捉 BlockException
来自定义被限流之后的处理逻辑。
类名 | 作用 |
---|---|
AuthorityException | 授权规则不通过 |
DegradeException | 服务降级 |
FlowException | 接口限流 |
ParamFlowException | 热点参数限流 |
SystemBlockException | 系统规则(负载不满足要求) |
# 3.3.1 阈值类型
- QPS——当调用这个API的QPS达到阈值的时候开始限流
- 线程数——当调用这个API的线程数达到阈值的时候开始限流
# 3.3.2 流控模式
- 直接
- 关联——当关联的资源达到阈值,就限流自己。
- 链路——只记录指定链路上的流量。
# 3.3.3 流控效果
- 快速失败——默认的流量控制方式,直接抛异常
- Warm Up (opens new window)
- 预热时长——即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。根据
codeFactor
(默认是3)的值,从阈值
/codeFactor
,经过预热时长,才达到设置的QPS阈值。
- 预热时长——即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。根据
- 排队等待 (opens new window)
- 超时时间——以固定的间隔时间让请求通过。当请求到来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过(排队等待处理);若预期的通过时间超出最大排队时长,则直接拒接这个请求。
- 匀速排队模式暂时不支持 QPS > 1000 的场景。
🤯 什么场景下会使用Warm Up呢?
秒杀微服务,平时的并发并不是很高,但是在某个时刻,秒杀活动开始了。开始的瞬间,流量会激增,如果不做处理,一开始服务就崩溃了。所有要使用Warm Up。
# 3.4 流控规则示例
# 3.4.1 直接-快速失败
QPS > 1:
# 3.4.2 关联-快速失败
当 /actuator/sentinel
QPS > 1,限制 /shares/1
🤯 什么场景下会使用关联呢?
现在有两个API,一个是查询shares
表数据,一个是修改shares
表数据。高并发场景下,查询接口调用速度过快,就会影响修改接口的性能。反之亦然。这个时候我们就能根据需求衡量,希望优先读还是优先写。关联其实是保护关联资源的一种模式。
# 3.4.3 链路-快速失败 (API级别)
@Service
@Slf4j
public class TestService {
@SentinelResource("common")
public String common() {
log.info("common...");
return "common";
}
}
2
3
4
5
6
7
8
9
@Resource
private TestService testService;
@GetMapping("/test-a")
public String testA() {
testService.common();
return "test-a";
}
@GetMapping("/test-b")
public String testB() {
testService.common();
return "test-b";
}
2
3
4
5
6
7
8
9
10
11
12
13
对 common
进行流控
QPS > 1:
- /test-a 被线路
- /test-b 没有影响
对于 common
这个资源,它的QPS统计只针对 test-a
,不影响 test-b
。
# 3.5 降级规则 (opens new window) (断路器模式)
参考:https://cloud.tencent.com/developer/article/1708863 (opens new window)
Sentinel 提供以下几种熔断策略:(1.8.0及以上版本)
- 慢调用比例 (
SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。 - 异常比例 (
ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。 - 异常数 (
ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
# 3.6 热点规则 (opens new window)
# 3.7 系统规则 (opens new window)
系统规则支持以下的模式:
- Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的
maxQps * minRt
估算得出。设定参考值一般是CPU cores * 2.5
。 - CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
# 3.8 授权规则 (opens new window)
# 3.9 代码配置规则
参考:Alibaba Sentinel 规则参数总结 (opens new window)
# 3.10 Sentienl与控制台通信原理
GET: http://192.168.31.208:8719/api
相关源码:
- 注册/心跳发送
- com.alibaba.csp.sentinel.transport.heartbeat.SimpleHttpHeartbeatSender
- 通信API
- com.alibaba.csp.sentinel.command.CommandHandler 的实现类
# 3.11 控制台相关配置项
- 应用端连接控制台配置项
spring:
cloud:
sentinel:
transport:
#配置sentinel dashboard的地址
dashboard: 127.0.0.1:8080
#指定和控制台通信的ip,如不配置,会自动选择一个ip注册
client-ip: 127.0.0.1
#指定和控制通信的端口,默认值8719,如不设置,会自动从8719开始扫描,依次+1,直到找到未被占用的端口
port: 8719
#心跳发送周期,默认值null,但在SimpleHttpHeartbeatSender会用默认值10秒
heartbeat-interval-ms: 10000
filter:
#关闭对spring mav端点的保护
enabled: false
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 3.12 基础API
- Entry SphU.entry(String name)
- void Tracer.trace(Throwable e)
- Context ContextUtil.enter(String name, String origin)
# 3.13 SentinelResource注解 (opens new window)
注意:注解方式埋点不支持 private 方法。
@SentinelResource
用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource
注解包含以下属性:
value
:资源名称,必需项(不能为空)entryType
:entry 类型,可选项(默认为EntryType.OUT
)blockHandler
/blockHandlerClass
:blockHandler
对应处理BlockException
的函数名称,可选项。blockHandler 函数访问范围需要是public
,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为BlockException
。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定blockHandlerClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析。fallback
/fallbackClass
:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore
里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:- 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要和原函数一致,或者可以额外多一个
Throwable
类型的参数用于接收对应的异常。 - fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
fallbackClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析。
defaultFallback
(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore`里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:- 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要为空,或者可以额外多一个
Throwable
类型的参数用于接收对应的异常。 - defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
fallbackClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析。
exceptionsToIgnore
(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
1.8.0 版本开始,defaultFallback
支持在类级别进行配置。
注:1.6.0 之前的版本 fallback 函数只针对降级异常(
DegradeException
)进行处理,不能针对业务异常进行处理。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException
时只会进入 blockHandler
处理逻辑。若未配置 blockHandler
、fallback
和 defaultFallback
,则被限流降级时会将 BlockException
直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException
)。
# 3.14 RestTemplate整合Sentinel
# 初始化RestTemplate添加注解开启整合
@Bean
@LoadBalanced
@SentinelRestTemplate
public RestTemplate restTemplate() {return new RestTemplate();}
2
3
4
@Resource
private RestTemplate restTemplate;
@GetMapping("/test-rest-template-sentinel/{userId}")
public UserDTO test(@PathVariable Integer userId) {
return restTemplate.getForObject("http://user-center/users/{userId}", UserDTO.class, userId);
}
2
3
4
5
6
7
# 添加流控规则:
# QPS > 1 时:
# 🤯 关闭@SentinelRestTemplate注解
resttemplate:
sentinel:
#关闭@SentinelRestTemplate注解
enabled: false
2
3
4
# 3.15 Feign整合Sentinel
# 3.15.1 整合
feign.sentinel.enabled = true
# 3.15.2 自定义限流降级异常逻辑
FeignClient
中的fallback
属性指定处理类
@FeignClient(name = "user-center",fallback = UserCenterFeignClientFallback.class)
public interface UserCenterFeignClient {
@GetMapping("/users/{id}")
UserDTO findById(@PathVariable Integer id);
}
2
3
4
5
6
UserCenterFeignClientFallback
实现UserCenterFeignClient
@Component
public class UserCenterFeignClientFallback implements UserCenterFeignClient {
@Override
public UserDTO findById(Integer id) {
UserDTO userDTO = new UserDTO();
userDTO.setWxNickname("一个默认用户");
return userDTO;
}
}
2
3
4
5
6
7
8
9
如果限流了,就进入 UserCenterFeignClientFallback
中的 findById
方法
# 3.15.3 如果获取到异常
- 要想获取到异常,就不要是用
fallback
属性改使用fallbackFactor
属性
@FeignClient(name = "user-center",
fallbackFactory = UserCenterFeignClientFallbackFactory.class
)
2
3
UserCenterFeignClientFallbackFactory
实现FallbackFactory
@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;
}
};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 3.16 规则持久化——拉模式
# 3.16.1 原理
FileRefreshableDataSource 定时从指定文件中读取规则JSON文件【图中的本地文件】,如果发现文件发生变化,就更新规则缓存。
FileWritableDataSource 接收控制台规则推送,并根据配置,修改规则JSON文件【图中的本地文件】。
# 3.16.2 实现
- 加依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
2
3
4
- 编写
FileDataSourceInit.java
/**
* 拉模式规则持久化
*/
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 final Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<FlowRule>>() {
}
);
private final Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<DegradeRule>>() {
}
);
private final Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<SystemRule>>() {
}
);
private final Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<AuthorityRule>>() {
}
);
private final 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);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
- 在项目的
resources/META-INF/services
目录下创建文件,名为com.alibaba.csp.sentinel.init.InitFunc
,内容为:
top.banner.contentcenter.sentineltest.FileDataSourceInit
# 3.16.2 优缺点
- 优点
- 简单
- 缺点
- 由于规则是用 FileRefreshableDataSource 定时更新的,所以规则更新会有延迟。如果FileRefreshableDataSource定时时间过大,可能长时间延迟;如果FileRefreshableDataSource过小,又会影响性能;
- 规则存储在本地文件,如果有一天需要迁移微服务,那么需要把规则文件一起迁移,否则规则会丢失。
- 由于规则是用 FileRefreshableDataSource 定时更新的,所以规则更新会有延迟。如果FileRefreshableDataSource定时时间过大,可能长时间延迟;如果FileRefreshableDataSource过小,又会影响性能;
# 3.17 规则持久化——推模式
# 3.17.1 原理
控制台推送规则:
- 将规则推送到Nacos或其他远程配置中心
- Sentinel客户端链接Nacos,获取规则配置;并监听Nacos配置变化,如发生变化,就更新本地缓存(从而让本地缓存总是和Nacos一致)
控制台监听Nacos配置变化,如发生变化就更新本地缓存(从而让控制台本地缓存总是和Nacos一致)
# 3.17.2 实现 (opens new window)
# 3.17.3 优缺点
- 优点
- 规则持久化
- 一致性强
- 性能优秀
- 缺点
- 改动多,复杂
- 引入额外的依赖(Nacos)
# 3.18 集群流控 (opens new window)
# 3.19 错误页面优化
自定义一个 MyUrlBlockHandler.java
组件,实现 UrlBlockHandler.java
@Component
public class MyUrlBlockHandler implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws IOException {
ErrorMsg errorMsg = null;
if (e instanceof FlowException) {
errorMsg = ErrorMsg.builder()
.status(100)
.msg("限流了")
.build();
} else if (e instanceof DegradeException) {
errorMsg = ErrorMsg.builder()
.status(101)
.msg("降级了")
.build();
} else if (e instanceof ParamFlowException) {
errorMsg = ErrorMsg.builder()
.status(102)
.msg("热点参数限流")
.build();
} else if (e instanceof SystemBlockException) {
errorMsg = ErrorMsg.builder()
.status(103)
.msg("系统规则(负载/...不满足要求)")
.build();
} else if (e instanceof AuthorityException) {
errorMsg = ErrorMsg.builder()
.status(104)
.msg("授权规则不通过")
.build();
}
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Type","application/json;charset=utf-8");
response.setContentType("application/json;charset=utf-8");
new ObjectMapper().writeValue(response.getWriter(), errorMsg);
}
}
@Data
@Builder
class ErrorMsg {
private Integer status;
private String msg;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 3.20 实现区分来源
自定义一个 MyRequestOriginParser.java
组件,实现 RequestOriginParser.java
@Component
public class MyRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
// 从请求参数中获取名为 origin 的参数并返回
// 如果获取不到 origin 参数,那么就抛异常
// 可以放到Header中
String origin = request.getParameter("origin");
if (StringUtils.isBlank(origin)) {
throw new IllegalArgumentException("origin must be specified");
}
return origin;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
新增授权规则:
请求:
# 3.21 RESTFulURL支持
自定义一个 MyUrlCleaner.java
组件,实现 UrlCleaner.java
@Component
@Slf4j
public class MyUrlCleaner implements UrlCleaner {
@Override
public String clean(String originUrl) {
log.info("originUrl = {}", originUrl);
// 让/shares/1 与 /shares/2 的返回值相同
// 返回 /shares/{number}
String[] split = originUrl.split("/");
return Arrays.stream(split).map(string -> {
if (NumberUtils.isNumber(string)) {
return "{number}";
}
return string;
}).reduce((a, b) -> a + "/" + b)
.orElse(null);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
🤯 Sentinel清洗RESTful的@PathVariable (opens new window)
# 2.22 CommonFilter
- UrlBlockHandler:提供了Sentinel异常处理
- RequestOriginParser:来源支持
- UrlCleaner:重新定义资源名称
- 本质:CommonFilter
- 一、雪崩效应
- 二、常见的容错方案
- 三、Sentinel 分布式系统的流量防卫兵
- 3.1 在您的项目中引入 Sentinel
- 3.2 Sentinel 控制台
- 3.3 流控规则
- 3.4 流控规则示例
- 3.5 降级规则 (断路器模式)
- 3.6 热点规则
- 3.7 系统规则
- 3.8 授权规则
- 3.9 代码配置规则
- 3.10 Sentienl与控制台通信原理
- 3.11 控制台相关配置项
- 3.12 基础API
- 3.13 SentinelResource注解
- 3.14 RestTemplate整合Sentinel
- 3.15 Feign整合Sentinel
- 3.16 规则持久化——拉模式
- 3.17 规则持久化——推模式
- 3.18 集群流控
- 3.19 错误页面优化
- 3.20 实现区分来源
- 3.21 RESTFulURL支持
- 2.22 CommonFilter