前言
由于项目中使用了springcloudalibaba这套框架,使用sentinel做熔断降级功能,在Sentinel-Dashboard中配置规则是存储在内存之中,重启应用就会丢失,所以实际生产环境中需要配置规则的持久化实现。
搜了一下网上介绍规则持久化的文章,发现介绍的不是很全面,因此,在这里,我把我的时间经验分享出来。
Sentinel持久化介绍

存在三种持久化模式,生产环境一般使用push模式,该模式的工作原理如下图:
Sentinel-Dashboard统一管理配置,然后将规则统一推送到Nacos并持久化(生成配置文件),最后客户端监听Nacos,获取配置的Rule。
换句话说就是实现Sentinel Dashboard与Nacos之间的相互通信:
Sentinel-Dashboard界面配置流控规则—发布/推送—>Nacos生成配置文件并持久化;
通过Nacos配置文件修改流控规则—拉取—>Sentinel-Dashboard界面显示最新的流控规则。
目前官方提供的Sentinel Dashboard是不支持操作nacos的,配置的规则都是存放在内存的,因此,我们需要对Sentinel Dashboard进行改造。
下载Sentinel源代码
可以去github官网下载源码
使用idea打开界面如下:
我们可以看到Sentinel-Dashboard这个目录,这个就是sentinel的配置源码了
引入依赖
打开Sentinel-Dashboard目录的pom.xml文件,加入以下依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
修改代码
复制nacos配置文件
把src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos包下的文件复制到src/main/java/com/alibaba/csp/sentinel/dashboard/rule包下
在这个包下只有发布和读取流控的规则,我们要做降级规则持久化,就需要自己建发布和读取降级的规则如下:
/**
* @author Eric Zhao
* @since 1.4.0
*/
@Component("degradeRuleNacosProvider")
public class DegradeRuleNacosProvider implements DynamicRuleProvider<List<DegradeRuleEntity>> {
@Autowired
private ConfigService configService;
@Autowired
private Converter<String, List<DegradeRuleEntity>> converter;
@Override
public List<DegradeRuleEntity> getRules(String appName) throws Exception {
String rules = configService.getConfig(NacosConfigUtil.DESGRADE_DATA_ID_POSTFIX,
appName, 3000);
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
@Component("degradeRuleNacosPublisher")
public class DegradeRuleNacosPublisher implements DynamicRulePublisher<List<DegradeRuleEntity>> {
@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;
}
configService.publishConfig(NacosConfigUtil.DESGRADE_DATA_ID_POSTFIX,
app, converter.convert(rules));
}
}
如下图:
修改NacosConfig文件
添加降级规则编码和解码方法,自定义产生了ConfigService对象,手动指定nacos的地址和指定的namespace
@Configuration
public class NacosConfig {
@Bean
public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
return s -> JSON.parseArray(s, FlowRuleEntity.class);
}
@Bean
public Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {
return s -> JSON.parseArray(s, DegradeRuleEntity.class);
}
@Bean
public ConfigService nacosConfigService() throws Exception {
Properties properties = new Properties();
properties.put("serverAddr", "172.16.88.8:8848");
properties.put("namespace", "sentinel");
return ConfigFactory.createConfigService(properties);
}
}
修改工具类NacosConfigUtil
增加如下配置:
public static final String GROUP_ID = "SENTINEL_GROUP";
public static final String FLOW_DATA_ID_POSTFIX = "flow-rules";
public static final String DESGRADE_DATA_ID_POSTFIX = "degrade-rules";
修改降级接口DegradeController
依赖注入
@Autowired
+ private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;
+
+ @Autowired
+ private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;
修改apiQueryMachineRules方法为,通过nacos查询规则:
public Result<List<DegradeRuleEntity>> apiQueryMachineRules(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<DegradeRuleEntity> rules = ruleProvider.getRules(app);
logger.info("app = {},读取降级规则完成", app);
if (rules != null && !rules.isEmpty()) {
for (DegradeRuleEntity entity : rules) {
entity.setApp(app);
}
}
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("queryApps error:", throwable);
return Result.ofThrowable(-1, throwable);
}
}
修改publishRules方法改为如下,将修改的规则发布到nacos
private boolean publishRules(String app, String ip, Integer port) {
List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
try {
logger.info("app = {},推送降级规则完成,rules = {}", app, JSONObject.toJSONString(rules));
rulePublisher.publish(app, rules);
} catch (Exception e) {
e.printStackTrace();
}
return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);
}
修改流控接口FlowControllerV1
依赖注入
@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
修改apiQueryMachineRules方法,将从内存查询规则改为从nacos查询
@GetMapping("/rules")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<FlowRuleEntity>> apiQueryMachineRules(@RequestParam String app,
@RequestParam String ip,
@RequestParam 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<FlowRuleEntity> rules = sentinelApiClient.fetchFlowRuleOfMachine(app, ip, port);
//rules = repository.saveAll(rules);
List<FlowRuleEntity> rules = ruleProvider.getRules(app);
logger.info("app = {},读取规则完成", app);
if (rules != null && !rules.isEmpty()) {
for (FlowRuleEntity entity : rules) {
entity.setApp(app);
if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) {
entity.setId(entity.getClusterConfig().getFlowId());
}
}
}
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("Error when querying flow rules", throwable);
return Result.ofThrowable(-1, throwable);
}
}
修改publishRules方法,将规则发布到内存改为发布到nacos
private CompletableFuture<Void> publishRules(String app, String ip, Integer port) {
List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
try {
logger.info("app={},推送规则完成", app);
rulePublisher.publish(app, rules);
} catch (Exception e) {
e.printStackTrace();
}
return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules);
}
到这里我们就对控制台改造好了。
改造应用程序通过nacos获取规则
这里的应用程序使用的是springcloudalibaba框架,引入sentinel的其他依赖部分已省略
引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
添加配置
feign.sentinel.enabled=true
spring.cloud.sentinel.transport.port=18087
spring.cloud.sentinel.transport.dashboard=http://192.168.88.124:8888
spring.cloud.sentinel.datasource.degrade.nacos.server-addr=${spring.cloud.nacos.config.server-addr}
spring.cloud.sentinel.datasource.degrade.nacos.dataId=degrade-rules
spring.cloud.sentinel.datasource.degrade.nacos.groupId=${spring.application.name}
spring.cloud.sentinel.datasource.degrade.nacos.namespace=sentinel
spring.cloud.sentinel.datasource.degrade.nacos.data-type=json
spring.cloud.sentinel.datasource.degrade.nacos.rule-type=degrade
到这里我们的程序就可以订阅nacos的熔断降级和限流的规则变化了。
效果图
添加降级规则
nacos查询降级规则

改造后代码下载
可以在https://download.csdn.net/download/weixin_45973130/59129508下载sentinelg-dashboard改造后的源码。