改造sentinel-dashboard,使用Nacos持久化规则(详细)

本文介绍了如何将 Sentinel 的 Dashboard 与 Nacos 集成,实现规则的持久化存储。主要步骤包括:在 pom.xml 文件中添加 Nacos 依赖,修改 application.properties 配置文件,创建多个包和类来处理不同类型的规则(如授权、降级、流控、网关),并实现与 Nacos 的交互,最后修改相关 Controller 中的方法以使用新的规则提供者和发布者。完成这些修改后,Sentinel Dashboard 就能将规则持久化到 Nacos 中了。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

要想改造 sentinel-dashboard,需要修改 dashboard 的源码。(本文基于sentinel 1.8.0)

先通过 Github 拉取 Sentinel 源码,GitHub 地址:https://github.com/alibaba/Sentinel

导入 IDEA,下载依赖,进入 sentinel-dashboard 模块,所有的修改都在该模块下。

修改 POM

想要将 dashboard 的规则持久化到 Nacos,需要添加一个依赖,源码中已经添加,不过只在测试环境下打包进去了。

在 pom.xml 文件中找到以下依赖,去掉 scope 或注释掉。

<!-- for Nacos rule publisher sample -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <!-- <scope>test</scope> -->
</dependency>

修改配置文件

找到配置文件 application.properties 文件,在末尾添加一下配置:

#Nacos Configurations
sentinel.nacos.serverAddr=localhost:8848
sentinel.nacos.namespace=
sentinel.nacos.group-id=SENTINEL-GROUP

sentinel.nacos.serverAddr 是 Nacos 服务器地址,如果是集群则用逗号隔开。

修改后端代码

新建包 com.alibaba.csp.sentinel.dashboard.rule.nacos,并在该包下根据以下结构新建包。

├─auth #存放授权规则相关类
├─degrade #存放降级规则相关类
├─flow #存放限流规则相关类
├─gateway #存放网关限流规则相关类
│  ├─api
│  └─flow
├─param #存放热点规则相关类
└─system #存放系统规则相关类

直接在com.alibaba.csp.sentinel.dashboard.rule.nacos包下创建以下类:

  1. NacosPropertiesConfig,存放前面配置文件中关于 Nacos 的配置
@ConfigurationProperties("sentinel.nacos")
public class NacosPropertiesConfig {
   
    private String serverAddr;
    private String dataId;
    private String groupId = "SENTINEL_GROUP"; // 默认分组
    private String namespace;

    public String getServerAddr() {
   
        return serverAddr;
    }

    public void setServerAddr(String serverAddr) {
   
        this.serverAddr = serverAddr;
    }

    public String getDataId() {
   
        return dataId;
    }

    public void setDataId(String dataId) {
   
        this.dataId = dataId;
    }

    public String getGroupId() {
   
        return groupId;
    }

    public void setGroupId(String groupId) {
   
        this.groupId = groupId;
    }

    public String getNamespace() {
   
        return namespace;
    }

    public void setNamespace(String namespace) {
   
        this.namespace = namespace;
    }
}
  1. NacosConfigUtil,存放一些常量,各种规则文件名后缀。
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 DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";
    public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
    public static final String SYS_DATA_ID_POSTFIX = "-system-rules";
    public static final String AUTH_DATA_ID_POSTFIX = "-auth-rules";

    public static final String GATEWAY_FLOW_DATA_ID_POSTFIX = "-gateway-flow";
    public static final String GATEWAY_API_DATA_ID_POSTFIX = "-gateway-api";

    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() {
   }
}
  1. NacosConfig
@EnableConfigurationProperties(NacosPropertiesConfig.class)
@Configuration
public class NacosConfig {
   

    //====================流控规则 Converter
    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
   
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
   
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    //====================降级规则 Converter
    @Bean
    public Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {
   
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {
   
        return s -> JSON.parseArray(s, DegradeRuleEntity.class);
    }

    //====================热点规则 Converter
    @Bean
    public Converter<List<ParamFlowRuleEntity>, String> paramsRuleEntityEncoder() {
   
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<ParamFlowRuleEntity>> paramsRuleEntityDecoder() {
   
        return s -> JSON.parseArray(s, ParamFlowRuleEntity.class);
    }

    //====================系统规则 Converter
    @Bean
    public Converter<List<SystemRuleEntity>, String> systemRuleEntityEncoder() {
   
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<SystemRuleEntity>> systemRuleEntityDecoder() {
   
        return s -> JSON.parseArray(s, SystemRuleEntity.class);
    }

    //====================授权规则 Converter
    @Bean
    public Converter<List<AuthorityRuleEntity>, String> authRuleEntityEncoder() {
   
        return JSON::toJSONString;
    }


    @Bean Converter<String,List<AuthorityRuleEntity>> authRuleEntityDecoder(){
   
        return s-> JSON.parseArray(s, AuthorityRuleEntity.class);
    }

    //====================网关限流规则 Converter
    @Bean
    public Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
   
        return JSON::toJSONString;
    }


    @Bean Converter<String,List<GatewayFlowRuleEntity>> gatewayFlowRuleEntityDecoder(){
   
        return s-> JSON.parseArray(s, GatewayFlowRuleEntity.class);
    }
    //====================网关API限流规则 Converter
    @Bean
    public Converter<List<ApiDefinitionEntity>, String> gatewayApiRuleEntityEncoder() {
   
        return JSON::toJSONString;
    }


    @Bean Converter<String,List<ApiDefinitionEntity>> gatewayApiRuleEntityDecoder(){
   
        return s-> JSON.parseArray(s, ApiDefinitionEntity.class);
    }


    @Bean
    public ConfigService nacosConfigService(NacosPropertiesConfig nacosPropertiesConfig) throws Exception {
   
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosPropertiesConfig.getServerAddr());
        properties.put(PropertyKeyConst.NAMESPACE, nacosPropertiesConfig.getNamespace());
        return ConfigFactory.createConfigService(properties);
    }
}

创建授权规则与 Nacos 交互类

在前面创建的auth包下创建以下两个类:

AuthorityRuleNacosProvider,内容如下:

@Component("authRuleNacosProvider")
public class AuthorityRuleNacosProvider implements DynamicRuleProvider<List<AuthorityRuleEntity>> {
   

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthorityRuleNacosProvider.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<AuthorityRuleEntity>> converter;

    @Override
    public List<AuthorityRuleEntity> getRules(String appName) throws Exception {
   
        String rules = configService.getConfig(appName + NacosConfigUtil.AUTH_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
        LOGGER.info("get auth rule from nacos, rules : {}", rules);
        if (StringUtil.isEmpty(rules)) {
   
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

AuthorityRuleNacosPublisher,内容如下:

@Component("authRuleNacosPublisher")
public class AuthorityRuleNacosPublisher implements DynamicRulePublisher<List<AuthorityRuleEntity>> {
   

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthorityRuleNacosPublisher.class);

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<AuthorityRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<AuthorityRuleEntity> rules) throws Exception {
   
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
   
            return;
        }
        String convertedRule = converter.convert(rules);
        LOGGER.info("sentinel dashboard publisher auth rules : {}", convertedRule);
        configService.publishConfig(app + NacosConfigUtil.AUTH_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, convertedRule);
    }
}

创建降级规则与 Nacos 交互类

和前面一样,在前面创建的degrade包下创建以下两个类:

DegradeRuleNacosProvider,内容如下:

@Component("degradeRuleNacosProvider")
public class DegradeRuleNacosProvider implements DynamicRuleProvider<List<DegradeRuleEntity>> {
   

    private static final Logger LOGGER = LoggerFactory.getLogger(DegradeRuleNacosProvider.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<DegradeRuleEntity>> converter;

    @Override
    public List<DegradeRuleEntity> getRules(String appName) throws Exception {
   
        String rules = configService.getConfig(appName + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
        LOGGER.info("get degrade rules from nacos, rules: {}", rules);
        if (StringUtil.isEmpty(rules)) {
   
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

DegradeRuleNacosPublisher,内容如下:

@Component("degradeRuleNacosPublisher")
public class DegradeRuleNacosPublisher implements DynamicRulePublisher<List<DegradeRuleEntity>> {
   

    private static final Logger LOGGER = LoggerFactory.getLogger(DegradeRuleNacosPublisher.class);

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<DegradeRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<DegradeRuleEntity> rules) throws Exception {
   
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
   
            return;
        }
        String convertedRule = converter.convert(rules);
        LOGGER.info("sentinel dashboard publish degrade rules: {}", convertedRule);
        configService.publishConfig(app + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, convertedRule);
    }
}

创建流控规则与 Nacos 交互类

在前面创建的flow包下创建以下两个类:

FlowRuleNacosProvider,内容如下:

@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
   

    private static final Logger LOGGER = LoggerFactory.getLogger(FlowRuleNacosProvider.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<FlowRuleEntity>> converter;

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
   
        String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, 3000);
        LOGGER.info("get flow rules from nacos, rules: {}", rules);
        if (StringUtil.isEmpty(rules)) {
   
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

FlowRuleNacosPublisher,内容如下:

@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
   

    private static final Logger LOGGER = LoggerFactory.getLogger(FlowRuleNacosPublisher.class);
    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<List<FlowRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
   
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
   
            return;
        }
        String convertedRule = converter.convert(rules);
        LOGGER.info("sentinel dashboard publish flow rules: {}", convertedRule);
        configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, convertedRule);
    }
}

创建网关流控规则与 Nacos 交互类

在前面创建的gateway.api包下创建以下两个类:

GatewayApiRuleNacosProvider,内容如下:

@Component("gatewayApiRuleNacosProvider")
public class GatewayApiRuleNacosProvider implements DynamicRuleProvider<List<ApiDefinitionEntity>> {
   
    private static final Logger LOGGER = LoggerFactory.getLogger(GatewayApiRuleNacosProvider.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<ApiDefinitionEntity>> converter;

    @Override
    public List<ApiDefinitionEntity> getRules(String appName) throws Exception {
   
        String rules = configService.getConfig(appName + NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
        LOGGER.info("get gateway api rules from nacos, rules: {}", rules);
        if (StringUtil.isEmpty(rules)) {
   
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

GatewayApiRuleNacosPublisher,内容如下:

@Component("gatewayApiRuleNacosPublisher")
public class GatewayApiRuleNacosPublisher implements DynamicRulePublisher<List<ApiDefinitionEntity>> {
   
    private static final Logger LOGGER = LoggerFactory.getLogger(GatewayApiRuleNacosPublisher.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<List<ApiDefinitionEntity>, String> converter;

    @Override
    public void publish(String app, List<ApiDefinitionEntity> rules) throws Exception {
   
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
   
            return;
        }
        String convertedRule = converter.convert(rules);
        LOGGER.info("sentinel dashboard publish gateway api rules: {}", convertedRule);
        configService.publishConfig(app + NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, convertedRule);
    }
}

gateway.flow包下创建以下两个类:

GatewayFlowRuleNacosProvider,内容如下:

@Component("gatewayFlowRuleNacosProvider")
public class GatewayFlowRuleNacosProvider implements DynamicRuleProvider<List<GatewayFlowRuleEntity>> {
   

    private static final Logger LOGGER = LoggerFactory.getLogger(GatewayFlowRuleNacosProvider.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<GatewayFlowRuleEntity>> converter;

    @Override
    public List<GatewayFlowRuleEntity> getRules(String appName) throws Exception {
   
        String rules = configService.getConfig(appName + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
        LOGGER.info("get gateway flow rules from nacos, rules: {}", rules);
        if (StringUtil.isEmpty(rules)) {
   
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

GatewayFlowRuleNacosPublisher,内容如下:

@Component("gatewayFlowRuleNacosPublisher")
public class GatewayFlowRuleNacosPublisher implements DynamicRulePublisher<List<GatewayFlowRuleEntity>> {
   
    private static final Logger LOGGER = LoggerFactory.getLogger(GatewayFlowRuleNacosPublisher.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<List<GatewayFlowRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<GatewayFlowRuleEntity> rules) throws Exception {
   
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
   
            return;
        }
        String convertedRule = converter.convert(rules);
        LOGGER.info("sentinel dashboard publish gateway flow rules: {}", convertedRule);
        configService.publishConfig(app + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, convertedRule);
    }
}

创建热点规则与 Nacos 交互类

在前面创建的param包下新建以下两个类:

ParamRuleNacosProvider,内容如下:

@Component("paramRuleNacosProvider")
public class ParamRuleNacosProvider implements DynamicRuleProvider<List<ParamFlowRuleEntity>> {
   

    private static final Logger LOGGER = LoggerFactory.getLogger(ParamRuleNacosProvider.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<ParamFlowRuleEntity>> converter;

    @Override
    public List<ParamFlowRuleEntity> getRules(String appName) throws Exception {
   
        String rules = configService.getConfig(appName + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
        LOGGER.info("get param rules from nacos, rules: {}", rules);
        if (StringUtil.isEmpty(rules)) {
   
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

ParamRuleNacosPublisher,内容如下:

@Component("paramRuleNacosPublisher")
public class ParamRuleNacosPublisher implements DynamicRulePublisher<List<ParamFlowRuleEntity>> {
   

    private static final Logger LOGGER = LoggerFactory.getLogger(ParamRuleNacosPublisher.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<List<ParamFlowRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<ParamFlowRuleEntity> rules) throws Exception {
   
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
   
            return;
        }
        String convertedRule = converter.convert(rules);
        LOGGER.info("sentinel dashboard publish param rules: {}", convertedRule);
        configService.publishConfig(app + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, convertedRule);
    }

}

创建系统规则与 Nacos 交互类

在前面创建的system包下新建以下两个类:

SystemRuleNacosProvider,内容如下:

@Component("systemRuleNacosProvider")
public class SystemRuleNacosProvider implements DynamicRuleProvider<List<SystemRuleEntity>> {
   

    private static final Logger LOGGER = LoggerFactory.getLogger(SystemRuleNacosProvider.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<SystemRuleEntity>> converter;

    @Override
    public List<SystemRuleEntity> getRules(String appName) throws Exception {
   
        String rules = configService.getConfig(appName + NacosConfigUtil.SYS_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
        LOGGER.info("get system rules from nacos, rules: {}", rules);
        if (StringUtil.isEmpty(rules)) {
   
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

SystemRuleNacosPublisher,内容如下:

@Component("systemRuleNacosPublisher")
public class SystemRuleNacosPublisher implements DynamicRulePublisher<List<SystemRuleEntity>> {
   

    private static final Logger LOGGER = LoggerFactory.getLogger(SystemRuleNacosPublisher.class);

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<List<SystemRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<SystemRuleEntity> rules) throws Exception {
   
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
   
            return;
        }
        String convertedRule = converter.convert(rules);
        LOGGER.info("sentinel dashboard publisher system rule : {}", convertedRule);
        configService.publishConfig(app + NacosConfigUtil.SYS_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, convertedRule);
    }
}

修改 Controller

  1. 修改 com.alibaba.csp.sentinel.dashboard.controller包下的AuthorityRuleController,内容如下:
@RestController
@RequestMapping(value = "/authority")
public class AuthorityRuleController {
   

    private final Logger logger = LoggerFactory.getLogger(AuthorityRuleController.class);

    @Autowired
    private SentinelApiClient sentinelApiClient;
    @Autowired
    private RuleRepository<AuthorityRuleEntity, Long> repository;

    @Autowired
    @Qualifier("authRuleNacosProvider")
    private DynamicRuleProvider<List<AuthorityRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("authRuleNacosPublisher")
    private DynamicRulePublisher<List<AuthorityRuleEntity>> rulePublisher;

    @GetMapping("/rules")
    @AuthAction(PrivilegeType.READ_RULE)
    public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app,
                                                                        @RequestParam String ip,
                                                                        @RequestParam Integer port) {
   
        if (StringUtil.isEmpty(app)) {
   
            return Result.ofFail(-1, "app cannot be null or empty");
        }
        if (StringUtil.isEmpty(ip)) {
   
            return Result.ofFail(-1, "ip cannot be null or empty")
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值