改造Sentinel Dashboard规则存储到Nacos
基于Sentinel Dashboard1.8.0版本改造Nacos存储规则
代码实现
下面直接来看看如何实现的具体改造步骤,这里参考了Sentinel Dashboard
源码中关于Nacos实现的测试用例。
第一步:修改pom.xml
中的sentinel-datasource-nacos的依赖,将test
删除,这样才能在主程序中使用。
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
第二步:前端资源修改

前端资源修改
- 找到
resources/app/scripts/controllers/identity.js
中的这段代码:
if (data.code === 0) { flowRuleDialog.close(); let url = '/dashboard/flow/' + $scope.app; $location.path(url); } else { alert('失败:' + data.msg); }
修改为:
if (data.code === 0) { flowRuleDialog.close(); let url = '/dashboard/v2/flow/' + $scope.app; $location.path(url); } else { alert('失败:' + data.msg); }
- 找到
resources/app/scripts/directives/sidebar/sidebar.html
中的这段代码:
<li ui-sref-active="active"> <a ui-sref="dashboard.flowV1({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i> 流控规则 </a> </li>
修改为:
<li ui-sref-active="active"> <a ui-sref="dashboard.flow({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i> 流控规则 </a> </li>
- 找到
resources/app/scripts/services/flow_service_v1.js
中的这段代码:
return $http({ url: '/v1/flow/rule', data: rule, method: 'POST' });
修改为:
return $http({ url: '/v2/flow/rule', data: rule, method: 'POST' });
- 找到
resources/app/views/flow_v2.html
中的这段代码:
<a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.flowV1({app: app})"> 回到单机页面 </a>
修改为:
<!--<a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.flowV1({app: app})"> 回到单机页面 </a>-->
-
重新构建静态资源文件
进入
webapp/resources
目录,执行:npm run build
命令,推荐使用Node 11.x版本,Node12版本构建会报错
第三步:在com.alibaba.csp.sentinel.dashboard.rule
包下新建一个nacos包,用来编写针对Nacos的扩展实现,新建文件截图如下:

Nacos扩展实现
具体代码如下:
@Component("authorityRuleNacosProvider") public class AuthorityRuleNacosProvider implements DynamicRuleProvider<List<AuthorityRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public List<AuthorityRuleEntity> getRules(String appName) throws Exception { return nacosConfigUtil.getRuleEntitiesFromNacos( this.configService, appName, NacosConfigUtil.AUTHORITY_DATA_ID_POSTFIX, AuthorityRuleEntity.class ); } }
@Component("authorityRuleNacosPublisher") public class AuthorityRuleNacosPublisher implements DynamicRulePublisher<List<AuthorityRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public void publish(String app, List<AuthorityRuleEntity> rules) throws Exception { nacosConfigUtil.setRuleStringToNacos( this.configService, app, NacosConfigUtil.AUTHORITY_DATA_ID_POSTFIX, rules ); } }
@Component("degradeRuleNacosProvider") public class DegradeRuleNacosProvider implements DynamicRuleProvider<List<DegradeRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public List<DegradeRuleEntity> getRules(String appName) throws Exception { return nacosConfigUtil.getRuleEntitiesFromNacos( this.configService, appName, NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX, DegradeRuleEntity.class ); } }
@Component("degradeRuleNacosPublisher") public class DegradeRuleNacosPublisher implements DynamicRulePublisher<List<DegradeRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public void publish(String app, List<DegradeRuleEntity> rules) throws Exception { nacosConfigUtil.setRuleStringToNacos( this.configService, app, NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX, rules ); }
@Component("flowRuleNacosProvider") public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public List<FlowRuleEntity> getRules(String appName) throws Exception { return nacosConfigUtil.getRuleEntitiesFromNacos( this.configService, appName, NacosConfigUtil.FLOW_DATA_ID_POSTFIX, FlowRuleEntity.class ); } }
@Component("flowRuleNacosPublisher") public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public void publish(String app, List<FlowRuleEntity> rules) throws Exception { nacosConfigUtil.setRuleStringToNacos( this.configService, app, NacosConfigUtil.FLOW_DATA_ID_POSTFIX, rules ); } }
public class GatewatParamFlowRule extends ParamFlowRule { private long intervalSec = 1; private GatewayParamFlowItemEntity paramItem; public long getIntervalSec() { return intervalSec; } public void setIntervalSec(long intervalSec) { this.intervalSec = intervalSec; } public GatewayParamFlowItemEntity getParamItem() { return paramItem; } public void setParamItem(GatewayParamFlowItemEntity paramItem) { this.paramItem = paramItem; } @Override public String toString() { return "ParamFlowRule{" + "grade=" + super.getGrade() + ", paramIdx=" + super.getParamIdx() + ", count=" + super.getCount() + ", controlBehavior=" + super.getControlBehavior() + ", maxQueueingTimeMs=" + super.getMaxQueueingTimeMs() + ", burstCount=" + super.getBurstCount() + ", durationInSec=" + super.getDurationInSec() + ", paramFlowItemList=" + super.getParamFlowItemList() + ", clusterMode=" + super.isClusterMode() + ", clusterConfig=" + super.getClusterConfig() + ", paramItem=" + paramItem + ", intervalSec=" + intervalSec + '}'; } }
@Component("gatewayApiRuleNacosProvider") public class GatewayApiRuleNacosProvider implements DynamicRuleProvider<List<ApiDefinitionEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public List<ApiDefinitionEntity> getRules(String appName) throws Exception { return nacosConfigUtil.getRuleEntitiesFromNacos( this.configService, appName, NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX, ApiDefinitionEntity.class ); } }
@Component("gatewayApiRuleNacosPublisher") public class GatewayApiRuleNacosPublisher implements DynamicRulePublisher<List<ApiDefinitionEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public void publish(String app, List<ApiDefinitionEntity> rules) throws Exception { nacosConfigUtil.setRuleStringToNacos( this.configService, app, NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX, rules ); } }
@Component("gatewayFlowRuleNacosProvider") public class GatewayFlowRuleNacosProvider implements DynamicRuleProvider<List<GatewayFlowRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public List<GatewayFlowRuleEntity> getRules(String appName) throws Exception { return nacosConfigUtil.getRuleEntitiesFromNacos( this.configService, appName, NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX, GatewayFlowRuleEntity.class ); } }
@Component("gatewayFlowRuleNacosPublisher") public class GatewayFlowRuleNacosPublisher implements DynamicRulePublisher<List<GatewayFlowRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public void publish(String app, List<GatewayFlowRuleEntity> rules) throws Exception { nacosConfigUtil.setRuleStringToNacos( this.configService, app, NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX, rules ); } }
@Component("paramFlowRuleNacosProvider") public class ParamFlowRuleNacosProvider implements DynamicRuleProvider<List<ParamFlowRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public List<ParamFlowRuleEntity> getRules(String appName) throws Exception { return nacosConfigUtil.getRuleEntitiesFromNacos( this.configService, appName, NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX, ParamFlowRuleEntity.class ); } }
@Component("paramFlowRuleNacosPublisher") public class ParamFlowRuleNacosPublisher implements DynamicRulePublisher<List<ParamFlowRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public void publish(String app, List<ParamFlowRuleEntity> rules) throws Exception { nacosConfigUtil.setRuleStringToNacos( this.configService, app, NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX, rules ); } }
@Component("systemRuleNacosProvider") public class SystemRuleNacosProvider implements DynamicRuleProvider<List<SystemRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public List<SystemRuleEntity> getRules(String appName) throws Exception { return nacosConfigUtil.getRuleEntitiesFromNacos( this.configService, appName, NacosConfigUtil.SYSTEM_DATA_ID_POSTFIX, SystemRuleEntity.class ); } }
@Component("systemRuleNacosPublisher") public class SystemRuleNacosPublisher implements DynamicRulePublisher<List<SystemRuleEntity>> { @Autowired private ConfigService configService; @Autowired private NacosConfigUtil nacosConfigUtil; @Override public void publish(String app, List<SystemRuleEntity> rules) throws Exception { nacosConfigUtil.setRuleStringToNacos( this.configService, app, NacosConfigUtil.SYSTEM_DATA_ID_POSTFIX, rules ); } }
@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 { NacosProperties nacosProperties = nacosProperties(); Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, nacosProperties.getServerAddr()); if (StringUtils.isNotBlank(nacosProperties.getNamespace())) { properties.put(PropertyKeyConst.NAMESPACE, nacosProperties.getNamespace()); } return ConfigFactory.createConfigService(properties); } @Bean @ConfigurationProperties(prefix = "spring.cloud.sentinel.datasource.nacos") public NacosProperties nacosProperties() { return new NacosProperties(); } public static class NacosProperties { private String serverAddr; private String namespace; private String groupId; // Getter/Setter方法 } }
@Component public class NacosConfigUtil { @Autowired private NacosConfig.NacosProperties nacosProperties; 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 GATEWAY_FLOW_DATA_ID_POSTFIX = "-gateway-flow-rules"; public static final String GATEWAY_API_DATA_ID_POSTFIX = "-gateway-api-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"; /** * 将规则序列化成JSON文本,存储到Nacos server中 * * @param configService nacos config service * @param app 应用名称 * @param postfix 规则后缀 eg.NacosConfigUtil.FLOW_DATA_ID_POSTFIX * @param rules 规则对象 * @throws NacosException 异常 */ public <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; } List<Rule> ruleForApp = rules.stream() .map(rule -> { RuleEntity rule1 = (RuleEntity) rule; //System.out.println(rule1.getClass()); Rule rule2 = rule1.toRule(); //System.out.println(rule2.getClass()); return rule2; }) .collect(Collectors.toList()); // 存储,给微服务使用 String dataId = genDataId(app, postfix); configService.publishConfig( dataId, nacosProperties.getGroupId(), JSON.toJSONString(ruleForApp) ); // 存储,给控制台使用 configService.publishConfig( dataId + DASHBOARD_POSTFIX, nacosProperties.getGroupId(), JSON.toJSONString(rules,true) ); } /** * 从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 <T> List<T> getRuleEntitiesFromNacos(ConfigService configService, String appName, String postfix, Class<T> clazz) throws NacosException { String rules = configService.getConfig( genDataId(appName, postfix) + DASHBOARD_POSTFIX, //genDataId(appName, postfix), nacosProperties.getGroupId(), 3000 ); if (StringUtil.isEmpty(rules)) { return new ArrayList<>(); } return JSON.parseArray(rules,clazz); } private static String genDataId(String appName, String postfix) { return appName + postfix; } }
第四步:规则存储采用Nacos实现,代码修改如下:

修改为Nacos存储规则
- 修改
FlowControllerV2.java
文件中的DynamicRuleProvider
和DynamicRulePublisher
注入的Bean,改为上面我们编写的针对Nacos的实现:
@Autowired @Qualifier("flowRuleNacosProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider; @Autowired @Qualifier("flowRuleNacosPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
-
修改
GatewayApiController.java、GatewayFlowRuleController.java、AuthorityRuleController.java、DegradeController.java、ParamFlowRuleController.java、SystemController.java
文件中的private SentinelApiClient sentinelApiClient;
替换成上面我们编写的针对Nacos的实现:@Autowired @Qualifier("gatewayApiRuleNacosProvider") private DynamicRuleProvider<List<ApiDefinitionEntity>> ruleProvider; @Autowired @Qualifier("gatewayApiRuleNacosPublisher") private DynamicRulePublisher<List<ApiDefinitionEntity>> rulePublisher; @GetMapping("/list.json") @AuthAction(AuthService.PrivilegeType.READ_RULE) public Result<List<ApiDefinitionEntity>> queryApis(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 { List<ApiDefinitionEntity> apis = ruleProvider.getRules(app); repository.saveAll(apis); return Result.ofSuccess(apis); } catch (Throwable throwable) { logger.error("queryApis error:", throwable); return Result.ofThrowable(-1, throwable); } } private boolean publishRules(String app, String ip, Integer port) throws Exception { List<ApiDefinitionEntity> rules = repository.findAllByApp(app); rulePublisher.publish(app, rules); return true; }
修改GatewayFlowRuleEntity.java文件中的toRule()方法:
@Override public Rule toRule() { GatewatParamFlowRule paramFlowRule = new GatewatParamFlowRule(); paramFlowRule.setResource(this.getResource()); paramFlowRule.setCount(this.getCount()); paramFlowRule.setGrade(this.getGrade()); paramFlowRule.setDurationInSec(calIntervalSec(this.getInterval(), this.getIntervalUnit())); paramFlowRule.setBurstCount(this.getBurst()); paramFlowRule.setControlBehavior(this.getControlBehavior()); if (this.getMaxQueueingTimeoutMs() != null) { paramFlowRule.setMaxQueueingTimeMs(this.getMaxQueueingTimeoutMs()); } //这里需要增加intervalSec参数,GatewayFlowRule需要,否则一直是默认值1 paramFlowRule.setIntervalSec(paramFlowRule.getDurationInSec()); GatewayParamFlowItemEntity gatewayItem = this.getParamItem(); paramFlowRule.setParamItem(gatewayItem); paramFlowRule.setParamIdx(0); return paramFlowRule; }
第五步:修改application.properties文件,新增Nacos服务相关配置:
sentinel.dashboard.version=${project.version} spring.cloud.sentinel.datasource.nacos.server-addr=http://127.0.0.1:8848 spring.cloud.sentinel.datasource.nacos.groupId=DEFAULT_GROUP spring.cloud.sentinel.datasource.nacos.namespace=45fd5650-df52-42ff-92c5-a09516123456