基于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的扩展实现,新建文件截图如下:
具体代码如下:
@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实现,代码修改如下:
- 修改
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