sentinel集成nacos
准备
- 在nacos上新增相关空间。此处省略。
- 先通过 Github 拉取 Sentinel 源码,GitHub 地址:https://github.com/alibaba/Sentinel
- 导入到IDEA中
- 进入 sentinel-dashboard 模块,所有的修改都在该模块下。
后端代码调整
修改pom.xml
大约在104行
1
2
3
4
5
6<!-- for Nacos rule publisher sample -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<!-- <scope>test</scope>-->
</dependency>找到以上依赖,将scope删除或者注释掉
2.修改配置文件
找到配置文件 application.properties 文件,在末尾添加一下配置:
1 | # 服务端口、控制台地址、名称 |
注意:如果需要将规则持久化到nacos的自定义命名空间下,namespace修改成对应的空间名id即可
调整包结构:复制一份test中的nacos到rule中
在com.alibaba.csp.sentinel.dashboard.rule包下创建nacos包,并在该包下根据以下结构新建包
1
2
3
4
5
6
7
8├─auth #存放授权规则相关类
├─degrade #存放降级规则相关类
├─flow #存放限流规则相关类
├─gateway #存放网关限流规则相关类
│ ├─api
│ └─flow
├─param #存放热点规则相关类
└─system #存放系统规则相关类目录介绍:
FlowRuleNacosProvider: 动态获取Nacos配置中心流控规则
FlowRuleNacosPublisher: publish上传流控规则到Nacos配置中心
NacosConfig: Nacos配置
NacosConfigUtils: 流控规则配置增加SentinelNacosProperties文件
在com.alibaba.csp.sentinel.dashboard.rule.nacos包下创建SentinelNacosProperties.java文件,代码如下:
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
60package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
public class SentinelNacosProperties {
private String serverAddr;
private String namespace;
private String username;
private String password;
private String groupId = "SENTINEL_GROUP";
public String getServerAddr() {
return serverAddr;
}
public void setServerAddr(String serverAddr) {
this.serverAddr = serverAddr;
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
}修改NacosConfig文件
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/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.*;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Properties;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public class NacosConfig {
// 注入刚新建的nacos配置文件
private SentinelNacosProperties sentinelNacosProperties;
//====================流控规则 Converter====================
public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
return JSON::toJSONString;
}
public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
return s -> JSON.parseArray(s, FlowRuleEntity.class);
}
//====================降级规则 Converter====================
public Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {
return JSON::toJSONString;
}
public Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {
return s -> JSON.parseArray(s, DegradeRuleEntity.class);
}
//====================热点规则 Converter====================
public Converter<List<ParamFlowRuleEntity>, String> paramsRuleEntityEncoder() {
return JSON::toJSONString;
}
public Converter<String, List<ParamFlowRuleEntity>> paramsRuleEntityDecoder() {
return s -> JSON.parseArray(s, ParamFlowRuleEntity.class);
}
//====================系统规则 Converter====================
public Converter<List<SystemRuleEntity>, String> systemRuleEntityEncoder() {
return JSON::toJSONString;
}
public Converter<String, List<SystemRuleEntity>> systemRuleEntityDecoder() {
return s -> JSON.parseArray(s, SystemRuleEntity.class);
}
//====================授权规则 Converter====================
public Converter<List<AuthorityRuleEntity>, String> authRuleEntityEncoder() {
return JSON::toJSONString;
}
Converter<String, List<AuthorityRuleEntity>> authRuleEntityDecoder() {
return s -> JSON.parseArray(s, AuthorityRuleEntity.class);
}
//====================网关API限流规则 Converter====================
public Converter<List<ApiDefinitionEntity>, String> apiDefinitionEntityEncoder() {
return JSON::toJSONString;
}
public Converter<String, List<ApiDefinitionEntity>> apiDefinitionEntityDecoder() {
return s -> JSON.parseArray(s, ApiDefinitionEntity.class);
}
//====================网关限流规则 Converter====================
public Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
return JSON::toJSONString;
}
public Converter<String, List<GatewayFlowRuleEntity>> gatewayFlowRuleEntityDecoder() {
return s -> JSON.parseArray(s, GatewayFlowRuleEntity.class);
}
public ConfigService nacosConfigService(SentinelNacosProperties sentinelNacosProperties) throws Exception {
Properties properties = new Properties();
// nacos 地址
properties.put(PropertyKeyConst.SERVER_ADDR, sentinelNacosProperties.getServerAddr());
// 命令空间
properties.put(PropertyKeyConst.NAMESPACE, sentinelNacosProperties.getNamespace());
// nacos 账号
properties.put(PropertyKeyConst.USERNAME, sentinelNacosProperties.getUsername());
// nacos 账号密码
properties.put(PropertyKeyConst.PASSWORD, sentinelNacosProperties.getPassword());
return ConfigFactory.createConfigService(properties);
}
}修改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
35package com.alibaba.csp.sentinel.dashboard.rule.nacos;
/**
* @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 PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";
public static final String SYS_DATA_ID_POSTFIX = "-system-rules";
public static final String AUTH_DATA_ID_POSTFIX = "-auth-rules";
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";
public static final String GATEWAY_FLOW_DATA_ID_POSTFIX = "-gateway-flow-rules";
public static final String GATEWAY_API_DATA_ID_POSTFIX = "-gateway-api-rules";
private NacosConfigUtil() {}
}在auth包中创建如下类:
AuthorityRuleNacosProvider 用于从nacos拉取配置
AuthorityRuleNacosPublisher 用于控制台向nacos推送配置
参照上面拷贝的FlowRuleNacosProvider和FlowRuleNacosPublisher文件中的代码创建以上两个文件,完整的代码如下:
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
public class AuthorityRuleNacosProvider implements DynamicRuleProvider<List<AuthorityRuleEntity>> {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthorityRuleNacosProvider.class);
private ConfigService configService;
private Converter<String, List<AuthorityRuleEntity>> converter;
public List<AuthorityRuleEntity> getRules(String appName) throws Exception {
String rules = configService.getConfig(appName + NacosConfigUtil.AUTH_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, 3000);
LOGGER.info("get auth rule from nacos, rules : {}", rules);
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
public class AuthorityRuleNacosPublisher implements DynamicRulePublisher<List<AuthorityRuleEntity>> {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthorityRuleNacosPublisher.class);
private ConfigService configService;
private Converter<List<AuthorityRuleEntity>, String> converter;
public void publish(String app, List<AuthorityRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
String convertedRule = converter.convert(rules);
LOGGER.info("sentinel dashboard publisher auth rules : {}", convertedRule);
configService.publishConfig(app + NacosConfigUtil.AUTH_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, convertedRule, ConfigType.JSON.getType());
}
}依次创建降级规则、热点规则、系统规则等文件。
代码大同小异,不同点就是
Converter<List<AuthorityRuleEntity>, String> converter
中的entity不同,具体的可以回看NacosConfig中的配置,另一个就是data-id不同。这里就不一一罗列代码了。修改后文件为:
controller层开启 Nacos 适配
修改 com.alibaba.csp.sentinel.dashboard.controller.v2包下的FlowControllerV2,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13// @Autowired
// @Qualifier("flowRuleDefaultProvider")
// private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
// @Autowired
// @Qualifier("flowRuleDefaultPublisher")
// private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;注释掉原来的ruleProvider和rulePublisher,引入我们自己创建的。
AuthorityRuleController:
修改 com.alibaba.csp.sentinel.dashboard.controlle包下的AuthorityRuleController,内容如下:
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// 增加以下代码
private DynamicRuleProvider<List<AuthorityRuleEntity>> ruleProvider;
private DynamicRulePublisher<List<AuthorityRuleEntity>> rulePublisher;
public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine( String app,
String ip,
{ Integer port)
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
if (StringUtil.isEmpty(ip)) {
return Result.ofFail(-1, "ip cannot be null or empty");
}
if (port == null || port <= 0) {
return Result.ofFail(-1, "Invalid parameter: port");
}
if (!appManagement.isValidMachineOfApp(app, ip)) {
return Result.ofFail(-1, "given ip does not belong to given app");
}
try {
// 注释掉下面一行代码
// List<AuthorityRuleEntity> rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port);
// 增加以下代码
List<AuthorityRuleEntity> rules = ruleProvider.getRules(app);
// 尤其注意增加以下if逻辑,不然运行时会报错,这里踩的坑
if (rules != null && !rules.isEmpty()) {
for (AuthorityRuleEntity entity : rules) {
entity.setApp(app);
}
}
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("Error when querying authority rules", throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}
private boolean publishRules(String app, String ip, Integer port) {
List<AuthorityRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
// 注释掉这一行
// return sentinelApiClient.setAuthorityRuleOfMachine(app, ip, port, rules);
// 增加以下代码
try {
rulePublisher.publish(app, rules);
return true;
} catch (Exception e) {
return false;
}
}需要增加以及改动的代码,已经在代码上加了注释,请仔细阅读。
同理修改其他几个controller
注意:ParamFlowRule
1
2
3
4
5
6
7
8apiAddParamFlowRule中修改:
publishRules(entity.getApp(), entity.getIp(), entity.getPort());
private void publishRules(String app, String ip, Integer port) throws Exception {
List<ParamFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
rulePublisher.publish(app, rules);
}
前端代码调整
- sidebar.html
找到webapp/resources/app/scripts/directives/sidebar/sidebar.html文件,
(直接搜dashboard.flowV1
定位57行去掉V1
):
1 | <!--<li ui-sref-active="active" ng-if="!entry.isGateway"> |
- identity.js
找到webapp/resources/app/scripts/controllers/identity.js文件,
修改第4行:
1 | // 'FlowServiceV1' |
(直接搜/dashboard/flow/
定位第101行):
1 | // let url = '/dashboard/flow/' + $scope.app; |
- flow_v2.html 找到webapp/resources/app/views/flow_v2.html文件,找到以下代码:
1 | <a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.flowV1({app: app})"> |
将其注释掉或删掉。
最后这个回到单击页面 会恢复到 默认内存模式