已集成Nacos、Sentinel并且Sentinel dashboard可正常显示Nacos持久化的流控规则
Sentinel下载sentinel源码:https://github.com/alibaba/Sentinel/archive/refs/tags/1.8.3.zipsentinel-dashboard工程添加lombok依赖、修改Nacos scope新建或修改package、class、enum不再赘婿,详见class和enum的package路径!!已确认无误,复制粘贴当伸手党即可。
2.项目目录(下述代码全部在sentinel-dashboard工程) 3.修改文件:sidebar.htmlcom.alibaba.csp sentinel-datasource-nacos org.projectlombok lombok 1.18.16
文件目录:
替换内容:
修改identity.js为(目的:簇点链路添加流控规则调用API改为V2)
产生影响:
将流控规则时保存调用的API由V1改为V2(/v2/flow/rules)
4. 新增配置#在resources/application.properties中添加Sentinel -> Nacos的连接信息 #Nacos -> Sentinel:Nacos -> 服务 -> Sentinel dashboard sentinel.nacos.serverAddr=192.168.1.135:8847 sentinel.nacos.groupId=SENTINEL_GROUP sentinel.nacos.namespace=95c0ce18-50e0-4e1b-917e-6429d0c54c5e #dataId未使用 sentinel.nacos.dataId=5. 添加/修改同步代码(有问题看标题1)
添加完后目录:
实现思路借鉴test/com.alibaba.csp.sentinel.dashboard.rule.nacos
5.2 NacosPropertiesConfigurationpackage com.alibaba.csp.sentinel.dashboard.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix="sentinel.nacos")
public class NacosPropertiesConfiguration{
private String serverAddr;
private String groupId = "DEFAULT_GROUP";
private String namespace;
private String dataId;
}
5.3 NacosPropertiesConfiguration
package com.alibaba.csp.sentinel.dashboard.config;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum RuleTypeEnum {
FLOW("-flow-rules"),
DEGRADE("-degrade-rules"),
SYSTEM("-system-rules"),
AUTHORITY("-authority-rules"),
PARAM_FLOW("-param-flow-rules");
private String ruleTypePostfix;
}
5.1 nacos包
flow/degrade,system/authority/param-flow参考degrade实现NacosConfig
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.csp.sentinel.dashboard.config.NacosPropertiesConfiguration;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.*;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Properties;
@EnableConfigurationProperties(NacosPropertiesConfiguration.class)
@Configuration
public class NacosConfig {
@Bean
public Converter,String> flowRuleEntityEncoder(){
return JSON::toJSONString;
}
@Bean
public Converter> flowRuleDecoder(){
return s->JSON.parseArray(s,FlowRuleEntity.class);
}
@Bean
public Converter> degradeRuleDecoder(){
return s->JSON.parseArray(s,DegradeRuleEntity.class);
}
@Bean
public Converter> systemRuleDecoder(){
return s->JSON.parseArray(s,SystemRuleEntity.class);
}
@Bean
public Converter> authorityRuleDecoder(){
return s->JSON.parseArray(s,AuthorityRuleEntity.class);
}
@Bean
public Converter> paramFlowRuleDecoder(){
return s->JSON.parseArray(s,ParamFlowRuleEntity.class);
}
@Bean
public ConfigService nacosConfigService(NacosPropertiesConfiguration nacosPropertiesConfiguration) throws NacosException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.NAMESPACE,nacosPropertiesConfiguration.getNamespace());
properties.put(PropertyKeyConst.SERVER_ADDR,nacosPropertiesConfiguration.getServerAddr());
return ConfigFactory.createConfigService(properties);
}
}
NacosRuleApiProvider
package com.alibaba.csp.sentinel.dashboard.rule.nacos; public interface NacosRuleApiProvider{ T getRules(String app) throws Exception; }
NacosRuleApiPublisher
package com.alibaba.csp.sentinel.dashboard.rule.nacos; public interface NacosRuleApiPublisher{ void publish(String app, T rules) throws Exception; }
DegradeRuleNacosApiProvider
package com.alibaba.csp.sentinel.dashboard.rule.nacos.degrade;
import com.alibaba.csp.sentinel.dashboard.config.NacosPropertiesConfiguration;
import com.alibaba.csp.sentinel.dashboard.config.RuleTypeEnum;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosRuleApiProvider;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.nacos.api.config.ConfigService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Component("degradeProvider")
public class DegradeRuleNacosApiProvider implements NacosRuleApiProvider> {
@Autowired
private ConfigService configService;
@Autowired
private Converter> converter;
@Autowired
private NacosPropertiesConfiguration nacosPropertiesConfiguration;
@Override
public List getRules(String app) throws Exception {
String dataId = new StringBuilder(app).append(RuleTypeEnum.DEGRADE.getRuleTypePostfix()).toString();
String rules = configService.getConfig(dataId, nacosPropertiesConfiguration.getGroupId(), 3000);
log.info("从Nacos配置中心获取{}规则:{}", RuleTypeEnum.DEGRADE.name(), rules);
if (StringUtils.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
DegradeRuleNacosPublisher
package com.alibaba.csp.sentinel.dashboard.rule.nacos.degrade;
import com.alibaba.csp.sentinel.dashboard.config.NacosPropertiesConfiguration;
import com.alibaba.csp.sentinel.dashboard.config.RuleTypeEnum;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosRuleApiPublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Slf4j
@Component("degradePublisher")
public class DegradeRuleNacosPublisher implements NacosRuleApiPublisher> {
@Autowired
private ConfigService configService;
@Autowired
private Converter, String> converter;
@Autowired
private NacosPropertiesConfiguration nacosPropertiesConfiguration;
@Override
public void publish(String app, List rules) throws Exception {
AssertUtil.notEmpty(app, "appName 不能为空");
if (rules == null) {
return;
}
String dataId = new StringBuilder(app).append(RuleTypeEnum.DEGRADE.getRuleTypePostfix()).toString();
configService.publishConfig(dataId, nacosPropertiesConfiguration.getGroupId(), converter.convert(rules));
log.info("推送{}到Nacos配置中心,DataId:{},规则:{}", RuleTypeEnum.DEGRADE.name(), dataId, rules);
}
}
FlowRuleNacosApiProvider
package com.alibaba.csp.sentinel.dashboard.rule.nacos.flow;
import com.alibaba.csp.sentinel.dashboard.config.NacosPropertiesConfiguration;
import com.alibaba.csp.sentinel.dashboard.config.RuleTypeEnum;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosRuleApiProvider;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.nacos.api.config.ConfigService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Component("flowProvider")
public class FlowRuleNacosApiProvider implements NacosRuleApiProvider> {
@Autowired
private ConfigService configService;
@Autowired
private Converter> converter;
@Autowired
private NacosPropertiesConfiguration nacosPropertiesConfiguration;
@Override
public List getRules(String app) throws Exception {
String dataId = new StringBuilder(app).append(RuleTypeEnum.FLOW.getRuleTypePostfix()).toString();
String rules = configService.getConfig(dataId, nacosPropertiesConfiguration.getGroupId(), 3000);
log.info("从Nacos配置中心获取{}规则:{}", RuleTypeEnum.FLOW.name(), rules);
if (StringUtils.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
FlowRuleNacosPublisher
package com.alibaba.csp.sentinel.dashboard.rule.nacos.flow;
import com.alibaba.csp.sentinel.dashboard.config.NacosPropertiesConfiguration;
import com.alibaba.csp.sentinel.dashboard.config.RuleTypeEnum;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosRuleApiPublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Slf4j
@Component("flowPublisher")
public class FlowRuleNacosPublisher implements NacosRuleApiPublisher> {
@Autowired
private ConfigService configService;
@Autowired
private Converter, String> converter;
@Autowired
private NacosPropertiesConfiguration nacosPropertiesConfiguration;
@Override
public void publish(String app, List rules) throws Exception {
AssertUtil.notEmpty(app, "appName 不能为空");
if (rules == null) {
return;
}
String dataId = new StringBuilder(app).append(RuleTypeEnum.FLOW.getRuleTypePostfix()).toString();
configService.publishConfig(dataId, nacosPropertiesConfiguration.getGroupId(), converter.convert(rules));
log.info("推送{}到Nacos配置中心,DataId:{},规则:{}", RuleTypeEnum.FLOW.name(), dataId, rules);
}
}
5.4 FlowControllerV2
//改动:指定依赖注入类型
@Autowired
@Qualifier("flowProvider")
private NacosRuleApiProvider> ruleProvider;
@Autowired
@Qualifier("flowPublisher")
private NacosRuleApiPublisher> rulePublisher;
5.5 DegradeController
package com.alibaba.csp.sentinel.dashboard.controller;
import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosRuleApiPublisher;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.List;
@RestController
@RequestMapping("/degrade")
public class DegradeController {
private final Logger logger = LoggerFactory.getLogger(DegradeController.class);
@Autowired
private RuleRepository repository;
@Autowired
private SentinelApiClient sentinelApiClient;
@Autowired
@Qualifier("degradePublisher")
NacosRuleApiPublisher publisher;
@GetMapping("/rules.json")
@AuthAction(PrivilegeType.READ_RULE)
public Result> 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 rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port);
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("queryApps error:", throwable);
return Result.ofThrowable(-1, throwable);
}
}
@PostMapping("/rule")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result apiAddRule(@RequestBody DegradeRuleEntity entity) {
Result checkResult = checkEntityInternal(entity);
if (checkResult != null) {
return checkResult;
}
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
try {
entity = repository.save(entity);
publishRules(entity.getApp());
} catch (Throwable t) {
logger.error("Failed to add new degrade rule, app={}, ip={}", entity.getApp(), entity.getIp(), t);
return Result.ofThrowable(-1, t);
}
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
logger.warn("Publish degrade rules failed, app={}", entity.getApp());
}
return Result.ofSuccess(entity);
}
@PutMapping("/rule/{id}")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result apiUpdateRule(@PathVariable("id") Long id,
@RequestBody DegradeRuleEntity entity) {
if (id == null || id <= 0) {
return Result.ofFail(-1, "id can't be null or negative");
}
DegradeRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null) {
return Result.ofFail(-1, "Degrade rule does not exist, id=" + id);
}
entity.setApp(oldEntity.getApp());
entity.setIp(oldEntity.getIp());
entity.setPort(oldEntity.getPort());
entity.setId(oldEntity.getId());
Result checkResult = checkEntityInternal(entity);
if (checkResult != null) {
return checkResult;
}
entity.setGmtCreate(oldEntity.getGmtCreate());
entity.setGmtModified(new Date());
try {
entity = repository.save(entity);
publishRules(entity.getApp());
} catch (Throwable t) {
logger.error("Failed to save degrade rule, id={}, rule={}", id, entity, t);
return Result.ofThrowable(-1, t);
}
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
logger.warn("Publish degrade rules failed, app={}", entity.getApp());
}
return Result.ofSuccess(entity);
}
@DeleteMapping("/rule/{id}")
@AuthAction(PrivilegeType.DELETE_RULE)
public Result delete(@PathVariable("id") Long id) {
if (id == null) {
return Result.ofFail(-1, "id can't be null");
}
DegradeRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null) {
return Result.ofSuccess(null);
}
try {
repository.delete(id);
publishRules(oldEntity.getApp());
} catch (Throwable throwable) {
logger.error("Failed to delete degrade rule, id={}", id, throwable);
return Result.ofThrowable(-1, throwable);
}
if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
logger.warn("Publish degrade rules failed, app={}", oldEntity.getApp());
}
return Result.ofSuccess(id);
}
private boolean publishRules(String app, String ip, Integer port) {
List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);
}
private Result checkEntityInternal(DegradeRuleEntity entity) {
if (StringUtil.isBlank(entity.getApp())) {
return Result.ofFail(-1, "app can't be blank");
}
if (StringUtil.isBlank(entity.getIp())) {
return Result.ofFail(-1, "ip can't be null or empty");
}
if (entity.getPort() == null || entity.getPort() <= 0) {
return Result.ofFail(-1, "invalid port: " + entity.getPort());
}
if (StringUtil.isBlank(entity.getLimitApp())) {
return Result.ofFail(-1, "limitApp can't be null or empty");
}
if (StringUtil.isBlank(entity.getResource())) {
return Result.ofFail(-1, "resource can't be null or empty");
}
Double threshold = entity.getCount();
if (threshold == null || threshold < 0) {
return Result.ofFail(-1, "invalid threshold: " + threshold);
}
Integer recoveryTimeoutSec = entity.getTimeWindow();
if (recoveryTimeoutSec == null || recoveryTimeoutSec <= 0) {
return Result.ofFail(-1, "recoveryTimeout should be positive");
}
Integer strategy = entity.getGrade();
if (strategy == null) {
return Result.ofFail(-1, "circuit breaker strategy cannot be null");
}
if (strategy < CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType()
|| strategy > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {
return Result.ofFail(-1, "Invalid circuit breaker strategy: " + strategy);
}
if (entity.getMinRequestAmount() == null || entity.getMinRequestAmount() <= 0) {
return Result.ofFail(-1, "Invalid minRequestAmount");
}
if (entity.getStatIntervalMs() == null || entity.getStatIntervalMs() <= 0) {
return Result.ofFail(-1, "Invalid statInterval");
}
if (strategy == RuleConstant.DEGRADE_GRADE_RT) {
Double slowRatio = entity.getSlowRatioThreshold();
if (slowRatio == null) {
return Result.ofFail(-1, "SlowRatioThreshold is required for slow request ratio strategy");
} else if (slowRatio < 0 || slowRatio > 1) {
return Result.ofFail(-1, "SlowRatioThreshold should be in range: [0.0, 1.0]");
}
} else if (strategy == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) {
if (threshold > 1) {
return Result.ofFail(-1, "Ratio threshold should be in range: [0.0, 1.0]");
}
}
return null;
}
private void publishRules( String app) throws Exception {
List rules = repository.findAllByApp(app);
publisher.publish(app, rules);
}
}
6. 构建启动DashboardApplication 7.效果展示
同步前:
同步后(服务本地配置与控制面板、Nacos配置):
控制面板
服务本地Nacos持久化



