1. 序言
sentinel数据默认保存在内存中,dashboard服务端和使用客户端均是如此。
客户端需要在启动时通过编码加载,改动时需要修改代码,无法做到配置化。
这里改造分为服务端和客户端的改造
- 服务端改造:从nacos中获取规则数据,保存时存入nacos中
- 客户端改造:启动时从nacos中获取数据
2. 改造
2.1. 服务端sentinel-dashboard
改造
这里只对push模式改造,其他两种因有不完全缺陷,也不适合生产,就不考虑了。
sentinel官方修改文档
主要修改建议
民间修改文档
push模式改造需要改造sentinel-dashboard
,dashboard应该与nacos交互存储,持久化至nacos中
2.1.1. 下载sentinel-dashboard
工程
https://github.com/alibaba/Sentinel
下载工程,拷贝出sentinel-dashboard
切换项目只能得到1.8.6的版本,这里使用1.8.6版本,正好也比1.8.1版本好改造
2.1.2. 改造项目
2.1.2.1. 修改依赖
pom文件中取消sentinel-datasource-nacos
的text属性,否则依赖只在测试下生效,之后拷贝文件会没有依赖
1 2 3 4 5
| <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
|
由于dashboard工程依赖父工程,这里没有拷贝父工程下来,因此将父工程的pom信息整合进来,得到以下pom
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 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-dashboard</artifactId> <version>1.8.6</version> <packaging>jar</packaging>
<properties> <project.version>1.8.6</project.version> <resource.delimiter>@</resource.delimiter> <spring.boot.version>2.5.12</spring.boot.version> <curator.version>4.0.1</curator.version> <fastjson.version>1.2.83_noneautotype</fastjson.version> <javax.annotation-api.version>1.3.2</javax.annotation-api.version>
<junit.version>4.12</junit.version> <mockito.version>2.28.2</mockito.version> <assertj.version>3.12.1</assertj.version> <awaitility.version>3.1.5</awaitility.version> <powermock.version>2.0.0</powermock.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.source.version>1.8</java.source.version> <java.target.version>1.8</java.target.version> <java.encoding>UTF-8</java.encoding> <maven.compiler.version>3.8.0</maven.compiler.version> <maven.surefire.version>2.22.1</maven.surefire.version> <maven.source.version>3.0.1</maven.source.version> <maven.javadoc.version>3.0.1</maven.javadoc.version> <maven.deploy.version>2.8.2</maven.deploy.version> <maven.gpg.version>1.6</maven.gpg.version> <maven.jacoco.version>0.8.3</maven.jacoco.version> <maven.jar.version>3.1.0</maven.jar.version> <maven.pmd.version>3.8</maven.pmd.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-extension</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-annotation-aspectj</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-annotation-cdi-interceptor</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-parameter-flow-control</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-extension</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-zookeeper</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-apollo</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-etcd</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-netty-http</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-spring-mvc</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-common</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-cluster-common-default</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-adapter</artifactId> <version>${project.version}</version> </dependency>
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-metric-exporter</artifactId> <version>${project.version}</version> </dependency>
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency>
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>${mockito.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>${assertj.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.awaitility</groupId> <artifactId>awaitility</artifactId> <version>${awaitility.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>java-hamcrest</artifactId> <version>2.0.0.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement>
<dependencies> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-web-servlet</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-parameter-flow-control</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-api-gateway-adapter-common</artifactId> <version>${project.version}</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring.boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> <version>${spring.boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${spring.boot.version}</version> <scope>test</scope> </dependency>
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
<dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency>
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4.5</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpasyncclient</artifactId> <version>4.1.3</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore-nio</artifactId> <version>4.4.6</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency>
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-openapi</artifactId> <version>1.2.0</version> <scope>test</scope> </dependency>
<dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>${curator.version}</version> <scope>test</scope> </dependency>
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.github.stefanbirkner</groupId> <artifactId>system-rules</artifactId> <version>1.16.1</version> <scope>test</scope> </dependency> </dependencies>
<build> <finalName>sentinel-dashboard</finalName> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <delimiters> <delimiter>${resource.delimiter}</delimiter> </delimiters> <useDefaultDelimiters>false</useDefaultDelimiters> </configuration> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring.boot.version}</version> <configuration> <fork>true</fork> <mainClass>com.alibaba.csp.sentinel.dashboard.DashboardApplication</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin>
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> <version>${maven.deploy.version}</version> <configuration> <skip>true</skip> </configuration> </plugin> </plugins>
<resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource>
<resource> <directory>src/main/webapp/</directory> <excludes> <exclude>resources/node_modules/**</exclude> </excludes> </resource> </resources> </build> </project>
|
有的会报红,不用管它,不影响项目运行
最后dashboard工程目录如下:

2.1.2.2. 拷贝文件
工程下sentinel-dashboard
的test目录下有nacos实现,拷贝至src下rule目录下,如图:

2.1.2.3. 修改流控规则
2.1.2.3.1. NacosConfigV2
将两个动态注入类DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider
和DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher
修改为nacos加载的那两个类

修改 resources/app/scripts/directives/sidebar/sidebar.html
改造流控规则这里,将55行附近dashboard.flowV1
改为dashboard.flow
1 2 3 4 5 6 7 8
|
<li ui-sref-active="active" ng-if="!entry.isGateway"> <a ui-sref="dashboard.flow({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i> 流控规则</a> </li>
|
2.1.2.3.3. 配置nacos
nacos中默认的流控规则配置为
- groupId: SENTINEL_GROUP
- dataId: {appName}-flow-rules ,比如应用名为 appA,则 dataId 为 appA-flow-rules
若要修改需要修改NacosConfigUtil
中的GROUP_ID
和FLOW_DATA_ID_POSTFIX
建议不要修改,要考虑客户端也要公用规则

这里其实相当于官方只给了一个非常简单的实现,甚至连自动配置都没有做,所以我们就得自己动手做成配置化了,我们总不能写死配置在应用里吧,这比硬编码还硬编码了。
而且sentinel-dashboard
的配置信息我们也想配置在nacos中。
nacos的配置如下,注意namespace、group、dataId

dataId为拼接上来的,{appName}-flow-rules

rule的格式同FlowRuleEntity保持一致,因为程序中是直接JSON.parseArray
处理的
具体可以见代码FlowRuleNacosProvider#getRules()
1 2 3 4 5 6 7 8 9
| @Override public List<FlowRuleEntity> getRules(String appName) throws Exception { String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX, NacosConfigUtil.GROUP_ID, 3000); if (StringUtil.isEmpty(rules)) { return new ArrayList<>(); } return converter.convert(rules); }
|
而converter为NacosConfig
注册的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Configuration public class NacosConfig {
@Bean public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() { return JSON::toJSONString; }
@Bean public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class); }
@Bean public ConfigService nacosConfigService() throws Exception { return ConfigFactory.createConfigService("http://113.31.109.7:8848"); } }
|
适当的改造 这里比较多的硬编码,没做配置,我们稍微修改
改造后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Configuration public class NacosConfig {
@Bean public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() { return JSON::toJSONString; }
@Bean public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class); } @Value("${sentinel.dashboard.datasource.nacos.server-addr}") private String serverAddr="localhost:8848"; @Bean public ConfigService nacosConfigService() throws Exception { return ConfigFactory.createConfigService(serverAddr); } }
|
2.1.2.3.4. 启动sentinel-dashboard
并配置
启动工程,查看流控规则,这里的流控规则加载是在点击流控规则菜单的时候加载,不用起客户端了

- 新增一个看看

- 再看nacos中

- 格式化后为:

- 再看看一个集群的
sentinel配置

nacos格式化后

3. 客户端改造
客户端原本是直接与dashboard服务端交互的,由于服务端已经直接和nacos交互了,因此客户端也改造成和Nacos交互。
这里也只处理push模式的情况,客户端通过注册监听器到nacos中的方式,nacos数据变化了通知客户端更新,这样就实现了dashboard修改数据最后客户端更新数据,且数据持久化在nacos中
3.1. pom
客户端不再与服务端直接交互,但是客户端首先要注册到dashboard中,因此还是需要sentinel-transport-simple-http
依赖
,否则进入sentinel的时候啥也没有,看不到应用
加入nacos 的pom
1 2 3 4
| <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
|
3.2. 配置改造
由于nacos中保存的dataId和groupId在dashboard中已经定义好了规则,客户端也需要按照其规则执行,因此拷贝规则相关的逻辑
NacosConfigUtil
1 2 3 4 5 6 7 8 9 10 11 12
| 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 CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map"; public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config"; 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() {} }
|
配置类,主要是替换原来硬编码加载的FlowRule,替换为从nacos中获取并注册监听
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
| @Configuration public class SentinelConfig implements InitializingBean {
@Bean SentinelResourceAspect sentinelResourceAspect(){ return new SentinelResourceAspect(); }
@Value("${sentinel.datasource.nacos.server-addr}") private String serverAddr="localhost:8848";
@Override public void afterPropertiesSet() throws Exception {
loadRules();
}
private void loadRules() { String projectName = ResourceBundle.getBundle("sentinel").getString("project.name");
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>( serverAddr, NacosConfigUtil.GROUP_ID, projectName+NacosConfigUtil.FLOW_DATA_ID_POSTFIX, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() { })); SentinelProperty<List<FlowRule>> sentinelProperty = flowRuleDataSource.getProperty(); FlowRuleManager.register2Property(sentinelProperty); } }
|
此处变化也好理解,从nacos配置中心去拿到配置,再解析就完了,同时注册到nacos的时候注册监听器,当nacos变化的时候会通知应用,从而应用修改自己的数据。
这里还做了一个配置,默认本地nacos
1 2
| @Value("${sentinel.datasource.nacos.server-addr}") private String serverAddr="localhost:8848";
|
可以启动sentinel-dashboard
测试一下

Jmeter测试一下

修改资源

Jmeter测试一下

至此,就完成了sentinel持久化到nacos的整个部分
4. 总结
- sentinel-dashboard总给人感觉是一个半成品,控制面板都做的差不多了,但是没有对后续数据持久化做扩展,官方在test中其实也有做了实现,但是就是没有把对应的地方改完。
- 除此之外,还有硬编码在给出的测试类中,没做自动话配置,也就是说即使用了官方给出的类还是需要做扩展。
- 我理解对于大佬来说后续其实很简单了,毕竟主要的东西都在,但是不知为啥就是没有实现完,不让开箱即用。
sentinel-datasource-nacos
此类也是,已经接上了nacos了,但是呢,group、dataId又没有和dashboard这里保持一致,要用户手动拼接规则,传入数据,对于新使用的开发者来说,又需要探索好久吧。这里本可以直接按照相同的规则处理好的……
- 到了push模式了,其实
sentinel-transport-simple-http
这个模块已经可以不要了,因为两者其实已经不用通讯了,但是刚开始客户端的懒加载逻辑(即第一次流控的时候才注册到dashboard中)又没有按照解耦的来修改,这里其实修改成启动dashboard的时候直接从nacos中获取就可以解决了,但是后续也没有做。
对此,最大的猜测还是,官方想让用商业化的云服务?