Yuizhi Blog

The palest ink is better than the best memory(烂键盘)

0%

Sentinel 规则 持久化到Nacos 二

这篇是接着上一篇来写Sentinel 规则持久化到Nacos ,上一篇已经把大部分的流程都走通,但是只把限流完成了,但还有有降级规则,热点规则,系统规则,授权规则没有改造,这篇就来改造这些.

创建包目录

这里打开Sentinel源码,找到sentinel-dashboard 下面的com.alibaba.csp.sentinel.dashboard.rule 文件夹,在上一篇里面在这里复制过一个nacos文件夹,先从这里开始

upload successful

在rule下面建立4个包文件,名字的就随便你们怎么取,可以参照我的命名

先改造下NacosConfigUtil

直接贴代码

1
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
/**
* @author Eric Zhao
* @since 1.4.0
*/
public final class NacosConfigUtil {

public static final String GROUP_ID = "SENTINEL_GROUP";

public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";
public static final String SYSTEM_DATA_ID_POSTFIX = "-system-rules";
public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-flow-rules";
public static final String AUTHORITY_DATA_ID_POSTFIX = "-authority-rules";
public static final String DASHBOARD_POSTFIX = "-dashboard";
public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";

/**
* cc for `cluster-client`
*/
public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config";
/**
* cs for `cluster-server`
*/
public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config";
public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config";
public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set";

private NacosConfigUtil() {}


/**
* 将规则序列化成JSON文本,存储到Nacos server中
*
* @param configService nacos config service
* @param app 应用名称
* @param postfix 规则后缀 eg.NacosConfigUtil.FLOW_DATA_ID_POSTFIX
* @param rules 规则对象
* @throws NacosException 异常
*/
public static <T> void setRuleStringToNacos(ConfigService configService, String app, String postfix, List<T> rules) throws NacosException {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
configService.publishConfig(app + postfix,
NacosConfigUtil.GROUP_ID,
JSON.toJSONString(rules)
);
}

/**
* 从Nacos server中查询响应规则,并将其反序列化成对应Rule实体
*
* @param configService nacos config service
* @param appName 应用名称
* @param postfix 规则后缀 eg.NacosConfigUtil.FLOW_DATA_ID_POSTFIX
* @param clazz 类
* @param <T> 泛型
* @return 规则对象列表
* @throws NacosException 异常
*/
public static <T> List<T> getRuleEntitiesFromNacos(ConfigService configService, String appName, String postfix, Class<T> clazz) throws NacosException {
String rules = configService.getConfig(appName+postfix, NacosConfigUtil.GROUP_ID,
3000
);
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
return JSON.parseArray(rules, clazz);
}

创建Provider和Publisher

Sentinel持久化最重要就是这个拉取和推送,从Nacos 拉取配置,推送配置到Nacos,限流这个拉取 推送是官方的示例,我们跟着这个示例去写其他的拉取推送,先把FlowRuleNacosProvider和FlowRuleNacosPublisher 放入flow文件夹里面,这两个类需要改动,因为上面我们改了NacosConfigUtil,参照下面的服务降级改造

upload successful

这里以服务降级为例,来完成示例流程,在degrade创建两个类 DegradeRuleNacosProvider和DegradeRuleNacosPublisher,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @author Yuizhi
* @date 2020/4/7 11:59
*/

@Component("degradeRuleNacosProvider")
public class DegradeRuleNacosProvider implements DynamicRuleProvider<List<DegradeRuleEntity>> {

@Autowired
private ConfigService configService;

@Override
public List<DegradeRuleEntity> getRules(String appName) throws Exception {
return NacosConfigUtil.getRuleEntitiesFromNacos(
this.configService,
appName,
NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,
DegradeRuleEntity.class
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* @author Yuizhi
* @date 2020/4/7 11:59
*/

@Component("degradeRuleNacosPublisher")
public class DegradeRuleNacosPublisher implements DynamicRulePublisher<List<DegradeRuleEntity>> {

@Autowired
private ConfigService configService;

@Override
public void publish(String app, List<DegradeRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
NacosConfigUtil.setRuleStringToNacos(
this.configService,
app,
NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,
rules
);
}
}

这里是参照另外一位博主的文章改造的。GitHub地址:https://github.com/eacdy/Sentinel-Dashboard-Nacos ,使用1.6.2-NACOS分支即可。

修改Controller

找到 com.alibaba.csp.sentinel.dashboard.controller 下面的DegradeController,加上前面创建的类引用

此处省略无数图片😂

本来想通过图来表达更改了那些,但需要修改的地方太多,所用直接贴代码算了,修改的地方我都做了注释

1
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
@Controller
@RequestMapping(value = "/degrade", produces = MediaType.APPLICATION_JSON_VALUE)
public class DegradeController {

private final Logger logger = LoggerFactory.getLogger(DegradeController.class);

@Autowired
@Qualifier("degradeRuleNacosProvider")
private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;
@Autowired
@Qualifier("degradeRuleNacosPublisher")
private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;

@Autowired
private InMemDegradeRuleStore repository;
@Autowired
private SentinelApiClient sentinelApiClient;

@ResponseBody
@RequestMapping("/rules.json")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<DegradeRuleEntity>> queryMachineRules(String app, String ip, Integer port) {

if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app can't be null or empty");
}
if (StringUtil.isEmpty(ip)) {
return Result.ofFail(-1, "ip can't be null or empty");
}
if (port == null) {
return Result.ofFail(-1, "port can't be null");
}
try {
// 这里进行了修改,使用ruleProvider 进行获取配置
List<DegradeRuleEntity> rules = ruleProvider.getRules(app);
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("queryApps error:", throwable);
return Result.ofThrowable(-1, throwable);
}
}

@ResponseBody
@RequestMapping("/new.json")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<DegradeRuleEntity> add(String app, String ip, Integer port, String limitApp, String resource,
Double count, Integer timeWindow, Integer grade) {
if (StringUtil.isBlank(app)) {
return Result.ofFail(-1, "app can't be null or empty");
}
if (StringUtil.isBlank(ip)) {
return Result.ofFail(-1, "ip can't be null or empty");
}
if (port == null) {
return Result.ofFail(-1, "port can't be null");
}
if (StringUtil.isBlank(limitApp)) {
return Result.ofFail(-1, "limitApp can't be null or empty");
}
if (StringUtil.isBlank(resource)) {
return Result.ofFail(-1, "resource can't be null or empty");
}
if (count == null) {
return Result.ofFail(-1, "count can't be null");
}
if (timeWindow == null) {
return Result.ofFail(-1, "timeWindow can't be null");
}
if (grade == null) {
return Result.ofFail(-1, "grade can't be null");
}
if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {
return Result.ofFail(-1, "Invalid grade: " + grade);
}
DegradeRuleEntity entity = new DegradeRuleEntity();
entity.setApp(app.trim());
entity.setIp(ip.trim());
entity.setPort(port);
entity.setLimitApp(limitApp.trim());
entity.setResource(resource.trim());
entity.setCount(count);
entity.setTimeWindow(timeWindow);
entity.setGrade(grade);
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
try {
entity = repository.save(entity);
publishRules(entity.getApp());
} catch (Throwable throwable) {
logger.error("add error:", throwable);
return Result.ofThrowable(-1, throwable);
}
return Result.ofSuccess(entity);
}

@ResponseBody
@RequestMapping("/save.json")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<DegradeRuleEntity> updateIfNotNull(Long id, String app, String limitApp, String resource,
Double count, Integer timeWindow, Integer grade) {
if (id == null) {
return Result.ofFail(-1, "id can't be null");
}
if (grade != null) {
if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {
return Result.ofFail(-1, "Invalid grade: " + grade);
}
}
DegradeRuleEntity entity = repository.findById(id);
if (entity == null) {
return Result.ofFail(-1, "id " + id + " dose not exist");
}

if (StringUtil.isNotBlank(app)) {
entity.setApp(app.trim());
}

if (StringUtil.isNotBlank(limitApp)) {
entity.setLimitApp(limitApp.trim());
}
if (StringUtil.isNotBlank(resource)) {
entity.setResource(resource.trim());
}
if (count != null) {
entity.setCount(count);
}
if (timeWindow != null) {
entity.setTimeWindow(timeWindow);
}
if (grade != null) {
entity.setGrade(grade);
}
Date date = new Date();
entity.setGmtModified(date);

// 这里根据publishRules 做了修改
try {
entity = repository.save(entity);
if (entity == null) {
return Result.ofFail(-1, "save entity fail");
}
publishRules(entity.getApp());
} catch (Throwable throwable) {
logger.error("save error:", throwable);
return Result.ofThrowable(-1, throwable);
}
return Result.ofSuccess(entity);
}

@ResponseBody
@RequestMapping("/delete.json")
@AuthAction(PrivilegeType.DELETE_RULE)
public Result<Long> delete(Long id) {
if (id == null || id <= 0) {
return Result.ofFail(-1, "id can't be null");
}

DegradeRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null) {
return Result.ofSuccess(null);
}

// 这里根据publishRules 做了修改
try {
repository.delete(id);
publishRules(oldEntity.getApp());
} catch (Throwable throwable) {
logger.error("delete error:", throwable);
return Result.ofThrowable(-1, throwable);
}
return Result.ofSuccess(id);
}

/**
* 这里是我修改过的地方
* @param app
* @throws Exception
*/
private void publishRules(/*@NonNull*/ String app) throws Exception {
List<DegradeRuleEntity> rules = repository.findAllByApp(app);
rulePublisher.publish(app, rules);
}
}

测试服务降级规则

启动nacos 启动改好的Sentinel 启动demo,demo的yml 配置要改成这样:

1
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

server:
port: 8089
spring:
application:
name: sentinel-demo
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8081
port: 8719
eager: true
datasource:
#这一级的名字随便取
flow:
nacos:
serverAddr: 127.0.0.1:8848
dataId: ${spring.application.name}-flow-rules
groupId: SENTINEL_GROUP
rule_type: flow
dataType: json
degrade:
nacos:
serverAddr: 127.0.0.1:8848
dataId: ${spring.application.name}-degrade-rules
groupId: SENTINEL_GROUP
rule_type: flow
dataType: json

这个配置加载,规律都是一样,我就不多说了,在Sentinel 里面的给/flow 加上降级规则

upload successful

去nacos 里面看看规则有没有同步到这里面

upload successful

规则已经同步到nacos 里面,来测试下规则是否生效,我们使用 JMeter 进行测试

upload successful

可以看到,已经触发了服务降级规则,说明改造成功,其他几个规则更改依葫芦画瓢,想学习Sentinel规则设置可以来看这篇文章https://mrbird.cc/,其实还有个东西没有讲,就是网关的规则同步,但我1.7.1版本的Sentinel 网关设置有问题,等这个网关没有问题了,我会自己修改看看,如果还是依葫芦画瓢,我就不出教程了,有变动的话,我会出一篇网关的教程,ByeBye🙃

参考链接:
https://github.com/alibaba/Sentinel/wiki
https://blog.csdn.net/lilizhou2008/article/details/97075236
https://mrbird.cc/Sentinel%E6%8E%A7%E5%88%B6%E5%8F%B0%E8%AF%A6%E8%A7%A3.html