sentinel官方提供了NacosDatasource,但是只是支持客户端从nacos读取规则加载到内存;
dashboard上修改了配置规则之后,内容中规则生效了,但是未持久化到nacos。
持久化到nacos有两种方案:
一是拉模式,类似官方提供的案例`FileWritableDataSource`;
二是推模式,切断客户端和控制台的连接,客户端不需要修改,控制台dashboard端修改源码,推送和读取规则的时候都操作nacos。
目前采用的是第一种拉模式实现,较为简单,基于官方的`Nacos`配置实现。
自动配置类:
package com.zhaochao.autoconfig;
import com.alibaba.cloud.sentinel.SentinelProperties;
import com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration;
import com.zhaochao.common.handler.SentinelNacosDataSourceHandler;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author zhaochao
* @date 2021/8/4 20:07
* @desc sentinel 规则持久化到 nacos 配置
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(SentinelAutoConfiguration.class)
public class SentinelNacosDataSourceAutoConfig {
@Bean
@ConditionalOnMissingBean
public SentinelNacosDataSourceHandler sentinelNacosDataSourceHandler(SentinelProperties sentinelProperties) {
return new SentinelNacosDataSourceHandler(sentinelProperties);
}
}
持久化处理器:
package com.zhaochao.common.handler;
import com.alibaba.cloud.sentinel.SentinelProperties;
import com.alibaba.cloud.sentinel.datasource.RuleType;
import com.alibaba.cloud.sentinel.datasource.config.DataSourcePropertiesConfiguration;
import com.alibaba.cloud.sentinel.datasource.config.NacosDataSourceProperties;
import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.SmartInitializingSingleton;
import java.util.List;
/**
* @author zhaochao
* @date 2021/8/4 19:39
* @desc sentinel 规则持久化到 nacos 处理器
*/
public class SentinelNacosDataSourceHandler implements SmartInitializingSingleton {
private final SentinelProperties sentinelProperties;
public SentinelNacosDataSourceHandler(SentinelProperties sentinelProperties) {
this.sentinelProperties = sentinelProperties;
}
@Override
public void afterSingletonsInstantiated() {
sentinelProperties.getDatasource().values().forEach(this::registryWriter);
}
private void registryWriter(DataSourcePropertiesConfiguration dataSourceProperties) {
final NacosDataSourceProperties nacosDataSourceProperties = dataSourceProperties.getNacos();
if (nacosDataSourceProperties == null) {
return;
}
final RuleType ruleType = nacosDataSourceProperties.getRuleType();
switch (ruleType) {
case FLOW:
WritableDataSource<List<FlowRule>> flowRuleWriter = new NacosWritableDataSource<>(nacosDataSourceProperties, JSON::toJSONString);
WritableDataSourceRegistry.registerFlowDataSource(flowRuleWriter);
break;
case DEGRADE:
WritableDataSource<List<DegradeRule>> degradeRuleWriter = new NacosWritableDataSource<>(nacosDataSourceProperties, JSON::toJSONString);
WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWriter);
break;
case PARAM_FLOW:
WritableDataSource<List<ParamFlowRule>> paramFlowRuleWriter = new NacosWritableDataSource<>(nacosDataSourceProperties, JSON::toJSONString);
ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWriter);
break;
case SYSTEM:
WritableDataSource<List<SystemRule>> systemRuleWriter = new NacosWritableDataSource<>(nacosDataSourceProperties, JSON::toJSONString);
WritableDataSourceRegistry.registerSystemDataSource(systemRuleWriter);
break;
case AUTHORITY:
WritableDataSource<List<AuthorityRule>> authRuleWriter = new NacosWritableDataSource<>(nacosDataSourceProperties, JSON::toJSONString);
WritableDataSourceRegistry.registerAuthorityDataSource(authRuleWriter);
break;
default:
break;
}
}
}
nacos持久化数据源:
package com.zhaochao.common.handler;
import com.alibaba.cloud.sentinel.datasource.config.NacosDataSourceProperties;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.zhaochao.common.util.LogUtil;
import org.springframework.util.StringUtils;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author zhaochao
* @date 2021/8/4 19:07
* @desc sentinel规则 规则持久化到 Nacos
*/
public class NacosWritableDataSource<T> implements WritableDataSource<T> {
private final Converter<T, String> configEncoder;
private final NacosDataSourceProperties nacosDataSourceProperties;
private final Lock lock = new ReentrantLock(true);
private ConfigService configService = null;
public NacosWritableDataSource(NacosDataSourceProperties nacosDataSourceProperties, Converter<T, String> configEncoder) {
if (configEncoder == null) {
throw new IllegalArgumentException("Config encoder cannot be null");
}
if (nacosDataSourceProperties == null) {
throw new IllegalArgumentException("Config nacosDataSourceProperties cannot be null");
}
this.configEncoder = configEncoder;
this.nacosDataSourceProperties = nacosDataSourceProperties;
final Properties properties = buildProperties(nacosDataSourceProperties);
try {
// 也可以直接注入NacosDataSource,然后反射获取其configService属性
this.configService = NacosFactory.createConfigService(properties);
} catch (NacosException e) {
LogUtil.sysErrLog("create configService failed.", e);
}
}
private Properties buildProperties(NacosDataSourceProperties nacosDataSourceProperties) {
Properties properties = new Properties();
if (!StringUtils.isEmpty(nacosDataSourceProperties.getServerAddr())) {
properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosDataSourceProperties.getServerAddr());
} else {
properties.setProperty(PropertyKeyConst.ACCESS_KEY, nacosDataSourceProperties.getAccessKey());
properties.setProperty(PropertyKeyConst.SECRET_KEY, nacosDataSourceProperties.getSecretKey());
properties.setProperty(PropertyKeyConst.ENDPOINT, nacosDataSourceProperties.getEndpoint());
}
if (!StringUtils.isEmpty(nacosDataSourceProperties.getNamespace())) {
properties.setProperty(PropertyKeyConst.NAMESPACE, nacosDataSourceProperties.getNamespace());
}
if (!StringUtils.isEmpty(nacosDataSourceProperties.getUsername())) {
properties.setProperty(PropertyKeyConst.USERNAME, nacosDataSourceProperties.getUsername());
}
if (!StringUtils.isEmpty(nacosDataSourceProperties.getPassword())) {
properties.setProperty(PropertyKeyConst.PASSWORD, nacosDataSourceProperties.getPassword());
}
return properties;
}
@Override
public void write(T value) throws Exception {
lock.lock();
// todo handle cluster concurrent problem
try {
String convertResult = configEncoder.convert(value);
if (configService == null) {
LogUtil.sysErrLog("configServer is null, can not continue.", null);
return;
}
final boolean published = configService.publishConfig(nacosDataSourceProperties.getDataId(), nacosDataSourceProperties.getGroupId(), convertResult);
if (!published) {
LogUtil.debug("sentinel {} publish to nacos failed.", nacosDataSourceProperties.getRuleType());
}
} finally {
lock.unlock();
}
}
@Override
public void close() throws Exception {
}
}
使用方式还是直接按照官方的就好:
spring:
application:
name: your app name
cloud:
...
sentinel:
transport:
dashboard: 127.0.0.1:8088
# port: 8853
datasource:
flow-rules:
nacos:
server-addr: @nacos.server.address@
namespace: @nacos.common.namespace@
dataId: ${spring.application.name}-${spring.profiles.active}-flow-rule.json
groupId: SENTINEL_RULE
data-type: json
rule-type: flow
...
这样就会将规则自动持久化到nacos了,直接修改nacos中的规则也会生效