官网 git: https://github.com/alibaba/Sentinel
微服务可以将流控配置放在nacos中,但dashboard修改的规则不能持久化。
本文将dashboard中的修改同步到nacos中,dashboard监控流控仍然从微服务客户端读取。
微服务客户端重启后会从nacos中读取数据,实现持久化。
主要思路: dashboard修改配置的时候,将数据发布到nacos中。注意dashboard中的是xxRuleEntity,而客户端读取nacos的是xxRule.
master分支 目前为sentinel-1.8.0为例
下载源码,idea打开。
找到sentinel-dashboard这个项目
在该项目下的pom.xml文件中找到:
<!-- for Nacos rule publisher sample -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<scope>test</scope>
</dependency>
将test注释掉。
展开test->java->com.alibaba.csp.sentinel.dashboard->rule->nacos
复制到src->java->com.alibaba.csp.sentinel.dashboard
- 主要文件
NacosCofig 服务配置 及convert
@Configuration
public class NacosConfig {
@Value("${sentinel.datasource.nacos.server-addr:localhost:8848}")
private String serverAddr;
@Value("${sentinel.datasource.nacos.enable:false}")
private boolean enable;
@Bean
public FlowRuleConvert flowRuleEntityEncoder() {
return new FlowRuleConvert();
}
@Bean
public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
return s -> JSON.parseArray(s, FlowRuleEntity.class);
}
@Bean
public ParamFlowRuleConvert paramFlowRuleEntityEncoder() {
return new ParamFlowRuleConvert();
}
@Bean
public Converter<String, List<ParamFlowRuleEntity>> paramFlowRuleEntityDecoder() {
return s -> JSON.parseArray(s, ParamFlowRuleEntity.class);
}
@Bean
public DegradeRuleConvert degradeRuleEntityEncoder() {
return new DegradeRuleConvert();
}
@Bean
public Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {
return s -> JSON.parseArray(s, DegradeRuleEntity.class);
}
@Bean
public ConfigService nacosConfigService() throws Exception {
return ConfigFactory.createConfigService(serverAddr);
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
}
默认配置 NacosConfigUtil
注意 GROUP_ID 、xx_DATA_ID_POSTFIX 需要微服务客户端的一致
public final class NacosConfigUtil {
// public static final String GROUP_ID = "SENTINEL_GROUP";
public static final String GROUP_ID = "DEFAULT_GROUP";
public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-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() {}
}
FlowRuleNacosPublisher 规则发布
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List> {
@Autowired
private ConfigService configService;
@Autowired
private FlowRuleConvert converter;
@Override
public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
boolean success = configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, converter.convert(rules));
if(!success){
throw new RuntimeException("publish to nacos fail");
}
}
}
ParamFlowRuleNacosPublisher
@Component("paramFlowRuleNacosPublisher")
public class ParamFlowRuleNacosPublisher implements DynamicRulePublisher<List> {
@Autowired
private ConfigService configService;
@Autowired
private ParamFlowRuleConvert converter;
@Override
public void publish(String app, List<ParamFlowRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
boolean success = configService.publishConfig(app + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, converter.convert(rules));
if(!success){
throw new RuntimeException("publish to nacos fail");
}
}
}
DegradeRuleNacosPublisher
@Component("degradeRuleNacosPublisher")
public class DegradeRuleNacosPublisher implements DynamicRulePublisher<List> {
@Autowired
private ConfigService configService;
@Autowired
private DegradeRuleConvert converter;
@Override
public void publish(String app, List<DegradeRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
boolean success = configService.publishConfig(app + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, converter.convert(rules));
if(!success){
throw new RuntimeException("publish to nacos fail");
}
}
}
- 转换类
FlowRuleConvert
public class FlowRuleConvert implements Converter<List, String> {
@Override
public String convert(List<FlowRuleEntity> flowRuleEntities) {
if(flowRuleEntities==null){
return null;
}
List<FlowRule> flowRules = new ArrayList<>();
for (FlowRuleEntity entity : flowRuleEntities) {
FlowRule rule = new FlowRule();
rule.setLimitApp(entity.getLimitApp());
rule.setResource(entity.getResource());
if(entity.getGmtCreate()!=null){
rule.setGrade(entity.getGrade());
}
if(entity.getCount()!=null){
rule.setCount(entity.getCount());
}
if(entity.getStrategy()!=null){
rule.setStrategy(entity.getStrategy());
}
rule.setRefResource(entity.getRefResource());
if(entity.getControlBehavior()!=null){
rule.setControlBehavior(entity.getControlBehavior());
}
if(entity.getWarmUpPeriodSec()!=null){
rule.setWarmUpPeriodSec(entity.getWarmUpPeriodSec());
}
if(entity.getMaxQueueingTimeMs()!=null){
rule.setMaxQueueingTimeMs(entity.getMaxQueueingTimeMs());
}
rule.setClusterMode(entity.isClusterMode());
rule.setClusterConfig(entity.getClusterConfig());
flowRules.add(rule);
}
return JSON.toJSONString(flowRules,true);
}
}
ParamFlowRuleConvert
DegradeRuleConvert
@Autowired
private NacosConfig nacosConfig;
@Autowired
private FlowRuleNacosPublisher flowRuleNacosPublisher;
/ 修改publish /
private CompletableFuture publishRules(String app, String ip, Integer port) throws Exception {
List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
if(nacosConfig.isEnable()){
flowRuleNacosPublisher.publish(app,rules);
}
return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules);
}
ParamFlowRuleController
@Autowired
private NacosConfig nacosConfig;
@Autowired
private ParamFlowRuleNacosPublisher paramFlowRuleNacosPublisher;
private CompletableFuture publishRules(String app, String ip, Integer port) throws Exception {
List<ParamFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
if(nacosConfig.isEnable()){
paramFlowRuleNacosPublisher.publish(app,rules);
}
return sentinelApiClient.setParamFlowRuleOfMachine(app, ip, port, rules);
}
DegradeController
@Autowired
private NacosConfig nacosConfig;
@Autowired
private DegradeRuleNacosPublisher degradeRuleNacosPublisher;
private boolean publishRules(String app, String ip, Integer port) {
List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
if(nacosConfig.isEnable()){
try {
degradeRuleNacosPublisher.publish(app,rules);
} catch (Exception e) {
logger.error("publishRules failed. ",e);
return false;
}
}
return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);
}
- 配置文件
nacos 配置开关 默认false
sentinel.datasource.nacos.enable=true
nacos 地址 默认 localhost:8848
sentinel.datasource.nacos.server-addr=localhost:8848
5 微服务端示例
配置文件
server:
port: 8081
spring:
application:
name: sentinel-spring-cloud-nacos
cloud:
sentinel:
transport:
dashboard: localhost:8080
eager: false
# sentinel nacos
datasource:
ds-flow:
nacos:
server-addr: 192.168.116.128:8848
dataId: ${spring.application.name}-flow-rules
groupId: DEFAULT_GROUP
dataType: json
rule-type: flow
ds-param:
nacos:
server-addr: 192.168.116.128:8848
dataId: ${spring.application.name}-param-rules
groupId: DEFAULT_GROUP
dataType: json
rule-type: param-flow
ds-degrade:
nacos:
server-addr: 192.168.116.128:8848
dataId: ${spring.application.name}-degrade-rules
groupId: DEFAULT_GROUP
dataType: json
rule-type: degrade
SentinelResource
@RestController
@RequestMapping("/api/sentinel")
public class SentinelDemoController {
@SentinelResource(value = "helloSentinel",blockHandler = "blockHandler",blockHandlerClass = SentinelBlockUtil.class )
@GetMapping("/helloSentinel")
public Object helloSentinel(){
return "hello sentinel. "+System.currentTimeMillis();
}
@SentinelResource(value = "helloSentinelParam",blockHandler = "oneParamBlockHandler",blockHandlerClass = SentinelBlockUtil.class)
@GetMapping("/helloSentinelParam")
public Object helloSentinelParam(String userId){
String data = "hello helloSentinelParam. userId:"+userId+" "+System.currentTimeMillis();
return data;
}
}
源码:https://github.com/lvzhyt/Sentinel/tree/dashboard-nacos
dashboard-nacos 分支 sentinel-dashboard
微服务客户端示例 sentinel-demo-spring-cloud-nacos