😊 @ 作者: 一恍过去
💖 @ 主页: https://blog.csdn.net/zhuocailing3390
🎊 @ 社区: Java技术栈交流
🎉 @ 主题: sentinel-1.8.7与nacos-2.3.0实现动态规则配置、双向同步
⏱️ @ 创作时间: 2024年04月2日
目录
1、为什么整合Nacos
默认情况下Sentinel
配置的规则是储存的内存中,在重新Sentinel服务
后,配置会显示,我们通过整合第三方中间件
实现,配置的持久化,比如使用Nacos
;
我们要实现Sentinel
与Nacos
的双向同步持久化,就需要对sentinel-dashboard
的源码包进行修改。
2、源码拉取
1、下载源码压缩包
在Sentinel-github下载需要版本的压缩包,比如Sentinel-1.8.7.zip
,或者直接从Git上将代码Clone
2、加载源码
将下载好的Sentinel-1.8.7.zip
解压,使用IDE
工具,打开sentinel-dashboard
工程,或者直接从Git上将代码Clone
3、创建公共配置
在进行规则代码修改之前需要创建Nacos配置文件,在com.alibaba.csp.sentinel.dashboard.rule
包下创建nacos
包,并且在包下创建四个类:RuleNacosConfig
、RuleNacosProvider
、RuleNacosPublisher
、NacosConfigUtil
RuleNacosConfig:
java
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class NacosConfig {
@Bean
public ConfigService nacosConfigService() throws Exception {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, "localhost:8848");
// properties.put(PropertyKeyConst.NAMESPACE, "xxx");
properties.put(PropertyKeyConst.USERNAME, "nacos");
properties.put(PropertyKeyConst.PASSWORD, "nacos");
return ConfigFactory.createConfigService(properties);
}
}
RuleNacosProvider:
java
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class RuleNacosProvider {
@Autowired
private ConfigService configService;
public String getRules(String dataId) throws Exception {
// 将服务名称设置为GroupId
return configService.getConfig(dataId, NacosConfigUtil.GROUP_ID, 3000);
}
}
RuleNacosPublisher:
java
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class RuleNacosPublisher {
@Autowired
private ConfigService configService;
public void publish(String dataId, String rules) {
try {
if (rules == null) {
return;
}
configService.publishConfig(dataId, NacosConfigUtil.GROUP_ID, rules);
} catch (NacosException e) {
throw new RuntimeException(e);
}
}
}
NacosConfigUtil:
java
package 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 GATEWAY_FLOW_DATA_ID_POSTFIX = "-gateway-rules";
public static final String AUTHORITY_FLOW_DATA_ID_POSTFIX = "-authority-rules";
public static final String SYSTEM_FLOW_DATA_ID_POSTFIX = "-system-rules";
public static final String DEGRADE_FLOW_DATA_ID_POSTFIX = "-degrade-rules";
public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-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";
private NacosConfigUtil() {
}
}
4、控制台规则配置
通过修改源码,实现流控规则、降级规则、热点规则、系统规则、授权规则
的持久化操作;
4.1、流控规则
修改FlowControllerV1:
将RuleNacosPublisher
和RuleNacosProvider
注入到FlowControllerV1
中
java
// 加入以下代码:
@Autowired
private RuleNacosProvider ruleProvider;
@Autowired
private RuleNacosPublisher rulePublisher;
修改读取逻辑:
java
// 修改位置如下:
List<FlowRuleEntity> rules = sentinelApiClient.fetchFlowRuleOfMachine(app, ip, port);
// 将上面代码修改为以下代码:
String ruleStr = ruleProvider.getRules(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX);
List<FlowRuleEntity> rules = new ArrayList<>();
if (ruleStr != null) {
rules = JSON.parseArray(ruleStr, FlowRuleEntity.class);
if (rules != null && !rules.isEmpty()) {
for (FlowRuleEntity entity : rules) {
entity.setApp(app);
}
}
}
修改推送逻辑:
java
// 1、修改位置如下:
private CompletableFuture<Void> publishRules(String app, String ip, Integer port) {
List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules);
}
// 将上面代码修改为以下代码:
private void publishRules(String app) {
List<FlowRuleEntity> rules = repository.findAllByApp(app);
String ruleStr = JSON.toJSONString(rules);
rulePublisher.publish(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX, ruleStr);
}
=======================================================================================
// 2、修改位置如下:有两处
publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get(5000, TimeUnit.MILLISECONDS);
// 将上面代码修改为以下代码:
publishRules(entity.getApp());
=======================================================================================
// 3、修改位置如下:
publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get(5000, TimeUnit.MILLISECONDS);
// 将上面代码修改为以下代码:
publishRules(oldEntity.getApp());
4.2、熔断降级规则
修改DegradeController:
将RuleNacosProvider
和RuleNacosPublisher
注入到DegradeController
中
java
// 加入以下代码:
@Autowired
private RuleNacosProvider ruleProvider;
@Autowired
private RuleNacosPublisher rulePublisher;
修改读取逻辑:
java
// 修改位置如下:
List<DegradeRuleEntity> rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port);
// 将上面代码修改为以下代码:
String ruleStr = ruleProvider.getRules(app + NacosConfigUtil.DEGRADE_FLOW_DATA_ID_POSTFIX);
List<DegradeRuleEntity> rules = new ArrayList<>();
if (ruleStr != null) {
rules = JSON.parseArray(ruleStr, DegradeRuleEntity.class);
if (rules != null && !rules.isEmpty()) {
for (DegradeRuleEntity entity : rules) {
entity.setApp(app);
}
}
}
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
修改推送逻辑:
java
// 1、修改位置如下:
private boolean publishRules(String app, String ip, Integer port) {
List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);
}
// 将上面代码修改为以下代码:
private boolean publishRules(String app) {
List<DegradeRuleEntity> rules = repository.findAllByApp(app);
String ruleStr = JSON.toJSONString(rules);
rulePublisher.publish(app + NacosConfigUtil.DEGRADE_FLOW_DATA_ID_POSTFIX, ruleStr);
return true;
}
=======================================================================================
// 2、修改位置如下:有两处
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
logger.warn("Publish degrade rules failed, app={}", entity.getApp());
}
// 将上面代码修改为以下代码:
if (!publishRules(entity.getApp())){
logger.warn("Publish degrade rules failed, app={}", entity.getApp());
}
=======================================================================================
// 3、修改位置如下:
if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
logger.warn("Publish degrade rules failed, app={}", oldEntity.getApp());
}
// 将上面代码修改为以下代码:
if (!publishRules(oldEntity.getApp())){
logger.warn("Publish degrade rules failed, app={}", entity.getApp());
}
4.3、热点规则
修改ParamFlowRuleController:
将RuleNacosProvider
和RuleNacosPublisher
注入到ParamFlowRuleController
中
java
// 加入以下代码:
@Autowired
private RuleNacosProvider ruleProvider;
@Autowired
private RuleNacosPublisher rulePublisher;
修改读取逻辑:
java
// 修改位置如下:
return sentinelApiClient.fetchParamFlowRulesOfMachine(app, ip, port)
.thenApply(repository::saveAll)
.thenApply(Result::ofSuccess)
.get();
// 将上面代码修改为以下代码:
String ruleStr = ruleProvider.getRules(app + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX);
List<ParamFlowRuleEntity> rules = new ArrayList<>();
if (ruleStr != null) {
rules = JSON.parseArray(ruleStr, ParamFlowRuleEntity.class);
if (rules != null && !rules.isEmpty()) {
for (ParamFlowRuleEntity entity : rules) {
entity.setApp(app);
}
}
}
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
修改推送逻辑:
java
// 1、修改位置如下:
private CompletableFuture<Void> publishRules(String app, String ip, Integer port) {
List<ParamFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setParamFlowRuleOfMachine(app, ip, port, rules);
}
// 将上面代码修改为以下代码:
private void publishRules(String app) {
try {
List<ParamFlowRuleEntity> rules = repository.findAllByApp(app);
String ruleStr = JSON.toJSONString(rules);
rulePublisher.publish(app + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX, ruleStr);
} catch (Exception e) {
e.printStackTrace();
}
}
=======================================================================================
// 2、修改位置如下:有两处
try {
entity = repository.save(entity);
publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get();
return Result.ofSuccess(entity);
} catch (ExecutionException ex) {
....
}
// 将上面代码修改为以下代码:
try {
entity = repository.save(entity);
publishRules(entity.getApp());
return Result.ofSuccess(entity);
} catch (Exception ex) {
....
}
=======================================================================================
// 3、修改位置如下:
try {
repository.delete(id);
publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get();
return Result.ofSuccess(id);
} catch (ExecutionException ex) {
....
}
// 将上面代码修改为以下代码:
try {
repository.delete(id);
publishRules(oldEntity.getApp());
return Result.ofSuccess(id);
} catch (Exception ex) {
....
}
4.4、系统规则
修改SystemController:
将RuleNacosProvider
和RuleNacosPublisher
注入到SystemController
中
java
// 加入以下代码:
@Autowired
private RuleNacosProvider ruleProvider;
@Autowired
private RuleNacosPublisher rulePublisher;
修改读取逻辑:
java
// 修改位置如下:
List<SystemRuleEntity> rules = sentinelApiClient.fetchSystemRuleOfMachine(app, ip, port);
// 将上面代码修改为以下代码:
String ruleStr = ruleProvider.getRules(app + NacosConfigUtil.SYSTEM_FLOW_DATA_ID_POSTFIX);
List<SystemRuleEntity> rules = new ArrayList<>();
if (ruleStr != null) {
rules = JSON.parseArray(ruleStr, SystemRuleEntity.class);
if (rules != null && !rules.isEmpty()) {
for (SystemRuleEntity entity : rules) {
entity.setApp(app);
}
}
}
修改推送逻辑:
java
// 1、修改位置如下:
private boolean publishRules(String app, String ip, Integer port) {
List<SystemRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setSystemRuleOfMachine(app, ip, port, rules);
}
// 将上面代码修改为以下代码:
private boolean publishRules(String app) {
List<SystemRuleEntity> rules = repository.findAllByApp(app);
String ruleStr = JSON.toJSONString(rules);
rulePublisher.publish(app + NacosConfigUtil.SYSTEM_FLOW_DATA_ID_POSTFIX, ruleStr);
return true;
}
=======================================================================================
// 2、修改位置如下
if (!publishRules(app, ip, port)) {
logger.warn("Publish system rules fail after rule add");
}
// 将上面代码修改为以下代码:
if (!publishRules(entity.getApp())) {
logger.warn("Publish system rules fail after rule add");
}
=======================================================================================
// 3、修改位置如下
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
logger.info("publish system rules fail after rule update");
}
// 将上面代码修改为以下代码:
if (!publishRules(entity.getApp())) {
logger.warn("Publish system rules fail after rule add");
}
=======================================================================================
// 4、修改位置如下:
if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
logger.info("publish system rules fail after rule delete");
}
// 将上面代码修改为以下代码:
if (!publishRules(oldEntity.getApp())) {
logger.info("publish system rules fail after rule delete");
}
4.5、授权规则
修改AuthorityRuleController:
将RuleNacosPublisher
和RuleNacosProvider
注入到AuthorityRuleController
中
java
// 加入以下代码:
@Autowired
private RuleNacosProvider ruleProvider;
@Autowired
private RuleNacosPublisher rulePublisher;
修改读取逻辑:
java
// 修改位置如下:
List<AuthorityRuleEntity> rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port);
// 将上面代码修改为以下代码:
String ruleStr = ruleProvider.getRules(app + NacosConfigUtil.SYSTEM_FLOW_DATA_ID_POSTFIX);
List<AuthorityRuleEntity> rules = new ArrayList<>();
if (ruleStr != null) {
rules = JSON.parseArray(ruleStr, AuthorityRuleEntity.class);
if (rules != null && !rules.isEmpty()) {
for (AuthorityRuleEntity entity : rules) {
entity.setApp(app);
}
}
}
修改推送逻辑:
java
// 1、修改位置如下:
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);
}
// 将上面代码修改为以下代码:
private boolean publishRules(String app) {
List<AuthorityRuleEntity> rules = repository.findAllByApp(app);
String ruleStr = JSON.toJSONString(rules);
rulePublisher.publish(app + NacosConfigUtil.AUTHORITY_FLOW_DATA_ID_POSTFIX, ruleStr);
return true;
}
=======================================================================================
// 2、修改位置如下:有两处
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
logger.info("Publish authority rules failed after rule update");
}
// 将上面代码修改为以下代码:
if (!publishRules(entity.getApp())) {
logger.info("Publish authority rules failed after rule update");
}
=======================================================================================
// 3、修改位置如下:
if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
logger.error("Publish authority rules failed after rule delete");
}
// 将上面代码修改为以下代码:
if (!publishRules(oldEntity.getApp())) {
logger.error("Publish authority rules failed after rule delete");
}
5、网关控制台规则配置
配置网关控制台规则,在启动网关时需要加上参数:-Dcsp.sentinel.app.type=1
本次源码修改中暂时没有做处理,如果需要可以参照下面的代码示例。
5.1、API管理
修改GatewayApiController:
将RuleNacosPublisher
和RuleNacosProvider
注入到GatewayApiController
中
java
// 加入以下代码:
@Autowired
private RuleNacosProvider ruleProvider;
@Autowired
private RuleNacosPublisher rulePublisher;
修改读取逻辑:
java
// 修改位置如下:
List<ApiDefinitionEntity> apis = sentinelApiClient.fetchApis(app, ip, port).get();
// 将上面代码修改为以下代码:
String ruleStr = ruleProvider.getRules(app + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX);
List<ApiDefinitionEntity> apis = new ArrayList<>();
if (ruleStr != null) {
apis = JSON.parseArray(ruleStr, ApiDefinitionEntity.class);
if (apis != null && !apis.isEmpty()) {
for (ApiDefinitionEntity entity : apis) {
entity.setApp(app);
}
}
}
修改推送逻辑:
java
// 1、修改位置如下:
private boolean publishApis(String app, String ip, Integer port) {
List<ApiDefinitionEntity> apis = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.modifyApis(app, ip, port, apis);
}
// 将上面代码修改为以下代码:
private boolean publishApi(String app) {
List<ApiDefinitionEntity> apis= repository.findAllByApp(app);
String ruleStr = JSON.toJSONString(apis);
rulePublisher.publish(app + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX, ruleStr);
return true;
}
=======================================================================================
// 2、修改位置如下
if (!publishApis(app, ip, port)) {
logger.warn("publish gateway apis fail after add");
}
// 将上面代码修改为以下代码:
if (!publishApis(entity.getApp())) {
logger.warn("publish gateway apis fail after add");
}
=======================================================================================
// 3、修改位置如下
if (!publishApis(app, entity.getIp(), entity.getPort())) {
logger.warn("publish gateway apis fail after update");
}
// 将上面代码修改为以下代码:
if (!publishApis(entity.getApp())
=======================================================================================
// 4、修改位置如下:
if (!publishApis(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
logger.warn("publish gateway apis fail after delete");
}
// 将上面代码修改为以下代码:
if (!publishApis(oldEntity.getApp())) {
logger.warn("publish gateway apis fail after delete");
}
5.2、流程规则
使用的是控制台规则配置中的流控规则接口,无需在做操作,参考《4.1、流控规则》。
5.3、降级规则
使用的是控制台规则配置中的降级规则接口,无需在做操作,参考《4.2、降级规则》。
5.4、系统规则
使用的是控制台规则配置中的系统规则接口,无需在做操作,参考《4.4、系统规则》。
6、效果测试
我们以流控规则
为例,演示一个数据同步持久化的操作;
1、nacos同步到sentinel:
需要注意的是,nacos中的分组必须为
SENTINEL_GROUP
在nacos中,新增配置
文件,文件的DataId为user-service-flow-rules
,内容为:
json
[
{
"app":"user-service",// 服务名称
"resource": "/list", //资源名称
"count": 1, //阀值
"grade": 1, //阀值类型,0表示线程数,1表示QPS;
"limitApp": "default", //来源应用
"strategy": 0,// 流控模式,0表示直接,1表示关联,2表示链路;
"controlBehavior": 0 //流控效果,0表示快速失败,1表示Warm Up,2表示排队等待
}
]
查看Sentinel控制台:数据已经实现了同步
2、sentinel同步到nacos:
我们在sentinel
控制台,建立任意流控规则,如下:
查看Nacos控制台:配置数据已经实现了同步
7、整合Cloud使用
整合cloud使用时,需要调整配置文件,以
流控规则
与热点规则
为例,其他的规则可以参考着进行配置,配置中的rule-type
、groupId
、dataId
需要写对(与nacos匹配)
yml
spring:
cloud:
sentinel:
transport:
port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可
dashboard: 127.0.0.1:8080 # 指定控制台服务的地址
#添加Nacos数据源配置
datasource:
#名字自定义即可,配置流控规则
sentinel-flow:
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
rule-type: flow
groupId: SENTINEL_GROUP
#namespace: public
dataId: user-service-flow-rules
#名字自定义即可,配置热点规则
sentinel-param:
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
rule-type: param-flow
groupId: SENTINEL_GROUP
#namespace: public
dataId: user-service-param-rules
8、打包部署
进入到sentinel-dashboard
所在目的,通过mvn clean install package -DskipTests=true
进行打包。
部署jar
参考:
《Linux搭建Sentinel 控制台环境》
《Docker搭建Sentinel 控制台环境》
9、源码包下载
对于上述修改的代码,源码下载地址:
https://gitee.com/lhzlx/sentinel-nacos.git