sentinel-1.8.7与nacos-2.3.0实现动态规则配置、双向同步

复制代码

😊 @ 作者: 一恍过去
💖 @ 主页: https://blog.csdn.net/zhuocailing3390
🎊 @ 社区: Java技术栈交流
🎉 @ 主题: sentinel-1.8.7与nacos-2.3.0实现动态规则配置、双向同步
⏱️ @ 创作时间: 2024年04月2日

目录

1、为什么整合Nacos

默认情况下Sentinel配置的规则是储存的内存中,在重新Sentinel服务后,配置会显示,我们通过整合第三方中间件实现,配置的持久化,比如使用Nacos

我们要实现SentinelNacos的双向同步持久化,就需要对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包,并且在包下创建四个类:RuleNacosConfigRuleNacosProviderRuleNacosPublisherNacosConfigUtil

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:

RuleNacosPublisherRuleNacosProvider注入到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:

RuleNacosProviderRuleNacosPublisher注入到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:

RuleNacosProviderRuleNacosPublisher注入到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:

RuleNacosProviderRuleNacosPublisher注入到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:

RuleNacosPublisherRuleNacosProvider注入到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:

RuleNacosPublisherRuleNacosProvider注入到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-typegroupIddataId需要写对(与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

相关推荐
东阳马生架构11 小时前
Nacos源码—9.Nacos升级gRPC分析四
nacos
RingWu1 天前
微服务架构-限流、熔断:Alibaba Sentinel入门
微服务·架构·sentinel
东阳马生架构2 天前
Nacos源码—8.Nacos升级gRPC分析三
nacos
东阳马生架构4 天前
Nacos源码—7.Nacos升级gRPC分析四
nacos·注册中心·配置中心
东阳马生架构4 天前
Nacos源码—7.Nacos升级gRPC分析三
nacos·注册中心·配置中心
东阳马生架构4 天前
Nacos源码—7.Nacos升级gRPC分析二
nacos
东阳马生架构5 天前
Nacos源码—6.Nacos升级gRPC分析一
nacos
gxh19926 天前
springboot微服务连接nacos超时
linux·后端·微服务·nacos
东阳马生架构6 天前
Nacos源码—5.Nacos配置中心实现分析二
nacos·注册中心·配置中心
东阳马生架构6 天前
Nacos源码—5.Nacos配置中心实现分析一
nacos·注册中心·配置中心