在springCloud架构下实现sentinel持久化到nacos
Posted liuec1002
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在springCloud架构下实现sentinel持久化到nacos相关的知识,希望对你有一定的参考价值。
文章目录
- 前言
- 代码
- 配置文件
- 改造sentinel控制台:
- 增加指定类 *在rule文件夹下增加nacos文件夹,在nacos文件夹下创建5个规则存储和拉取所需要的class,在nacos文件夹下增加NacosConfig和NacosConfigUtil配置类,在util中增加JSONUtils工具类。
- JSONUtils
- NacosConfig
- NacosConfigUtil
- AuthorityRuleNacosProvider
- AuthorityRuleNacosPublisher
- DegradeRuleNacosProvider
- DegradeRuleNacosPublisher
- FlowRuleNacosProvider
- FlowRuleNacosPublisher
- ParamFlowRuleNacosProvider
- ParamFlowRuleNacosPublisher
- SystemRuleNacosProvider
- SystemRuleNacosPublisher
- *修改5种规则crud的controller层 主要修改数据的存储,将原来存入内存的规则,现在存入nacos,并将nacos的数据push到sentinel的数据源中。
前言
一:sentinel持久化到nacos(全部配置)
二:sentinel控制台的改造(全部代码)
实现原理:
a) 控制台推送规则到Nacos/远程配置中心
b) Sentinel client 监听Nacos配置变化,更新本地缓存
配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel
代码
配置文件
```java
application-xxx.properties中增加配置
#jvm启动设置
#-Dproject.name=gh-demo-provide
#-Dcsp.sentinel.api.port=9000
#-Dcsp.sentinel.dashboard.server=127.0.0.1:8085
spring.application.name=gh-demo-provide
#同一服务器项目需要端用端口区分
spring.cloud.sentinel.transport.port=9000
#sentinel控制台地址
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8085
# sentinel dashboard
#流量控制
# nacos的访问地址,我这里是nigix代理nacos的集群地址
spring.cloud.sentinel.datasource.flow.nacos.server-addr=10.151.0.84:8848
#与sentinel控制台的namespeace统一
spring.cloud.sentinel.datasource.flow.nacos.namespace=16b7fa67-d3e0-47e4-bc0d-76056712afc4
#nacos中存储规则的dataId,对于dataId使用了$spring.application.name变量,这样可以根据应用名来区分不同的规则配置
spring.cloud.sentinel.datasource.flow.nacos.data-id=$spring.application.name-flow-rules
#nacos中存储规则的groupId
spring.cloud.sentinel.datasource.flow.nacos.group-id=SENTINEL_GROUP
#定义存储的规则类型
# 规则类型,取值见:
# org.springframework.cloud.alibaba.sentinel.datasource.RuleType
spring.cloud.sentinel.datasource.flow.nacos.rule-type=flow
#除流控以外的其他规则配置,可以选择性配置,也就是持久化哪个规则就配哪个
#熔断降级
spring.cloud.sentinel.datasource.degrade.nacos.server-addr=10.151.0.84:8848
spring.cloud.sentinel.datasource.degrade.nacos.namespace=16b7fa67-d3e0-47e4-bc0d-76056712afc4
spring.cloud.sentinel.datasource.degrade.nacos.data-id=$spring.application.name-degrade-rules
spring.cloud.sentinel.datasource.degrade.nacos.group-id=SENTINEL_GROUP
spring.cloud.sentinel.datasource.degrade.nacos.rule-type=degrade
#系统规则
spring.cloud.sentinel.datasource.system.nacos.server-addr=10.151.0.84:8848
spring.cloud.sentinel.datasource.system.nacos.namespace=16b7fa67-d3e0-47e4-bc0d-76056712afc4
spring.cloud.sentinel.datasource.system.nacos.data-id=$spring.application.name-system-rules
spring.cloud.sentinel.datasource.system.nacos.group-id=SENTINEL_GROUP
spring.cloud.sentinel.datasource.system.nacos.rule-type=system
#授权规则
spring.cloud.sentinel.datasource.authority.nacos.server-addr=10.151.0.84:8848
spring.cloud.sentinel.datasource.authority.nacos.namespace=16b7fa67-d3e0-47e4-bc0d-76056712afc4
spring.cloud.sentinel.datasource.authority.nacos.data-id=$spring.application.name-authority-rules
spring.cloud.sentinel.datasource.authority.nacos.group-id=SENTINEL_GROUP
spring.cloud.sentinel.datasource.authority.nacos.rule-type=authority
#热点规则
spring.cloud.sentinel.datasource.param-flow.nacos.server-addr=10.151.0.84:8848
spring.cloud.sentinel.datasource.param-flow.nacos.namespace=16b7fa67-d3e0-47e4-bc0d-76056712afc4
spring.cloud.sentinel.datasource.param-flow.nacos.data-id=$spring.application.name-param-flow-rules
spring.cloud.sentinel.datasource.param-flow.nacos.group-id=SENTINEL_GROUP
spring.cloud.sentinel.datasource.param-flow.nacos.rule-type=param-flow
改造sentinel控制台:
由于sentinel都将规则内容存入内存中,在重启服务或者5分钟之后,规则将被清除,所有这里修改sentinel控制台,使规则在nacos中实现增删改的储存,实现持久化。
sentinel控制台application.properties中加入:
application.properties中增加配置
server.port=8085
#nacos的集群地址
nacos.address=10.151.0.84:8848
#二选一public为空,namespeace为uuid,这里与dubbo服务一致
#nacos.namespace=
nacos.namespace=16b7fa67-d3e0-47e4-bc0d-76056712afc4
增加指定类 *在rule文件夹下增加nacos文件夹,在nacos文件夹下创建5个规则存储和拉取所需要的class,在nacos文件夹下增加NacosConfig和NacosConfigUtil配置类,在util中增加JSONUtils工具类。
增加类中具体的代码如下:
JSONUtils
public class JSONUtils
public static <T> String toJSONString(Object object)
try
return new ObjectMapper().writeValueAsString(object);
catch (JsonProcessingException e)
throw new IllegalArgumentException(e);
public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses)
return new ObjectMapper()
.getTypeFactory()
.constructParametricType(collectionClass, elementClasses);
public static <T> List<T> parseObject(Class<T> clazz, String string)
JavaType javaType = getCollectionType(ArrayList.class, clazz);
try
return (List<T>) new ObjectMapper().readValue(string, javaType);
catch (IOException e)
throw new IllegalArgumentException(e);
NacosConfig
@Configuration
public class NacosConfig
@Value("$nacos.address")
private String address;
@Value("$nacos.namespace")
private String namespace;
@Bean
public ConfigService nacosConfigService() throws Exception
Properties properties = new Properties();
//nacos集群地址
properties.put(PropertyKeyConst.SERVER_ADDR,address);
//namespace为空即为public
properties.put(PropertyKeyConst.NAMESPACE,namespace);
return ConfigFactory.createConfigService(properties);
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 SYSTEM_DATA_ID_POSTFIX = "-system-rules";
public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-flow-rules";
public static final String AUTHORITY_DATA_ID_POSTFIX = "-authority-rules";
public static final String DASHBOARD_POSTFIX = "-sentinel-dashboard";
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()
/**
*
* 将规则序列化成JSON文本,存储到Nacos server中
*
* @param configService nacos config service
* @param app 应用名称
* @param postfix 规则后缀 eg.NacosConfigUtil.FLOW_DATA_ID_POSTFIX
* @param rules 规则对象
* @throws NacosException 异常
*/
public static <T> void setRuleStringToNacos(ConfigService configService, String app, String postfix, List<T> rules) throws NacosException
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null)
return;
List<Rule> ruleForApp = rules.stream()
.map(rule ->
RuleEntity rule1 = (RuleEntity) rule;
System.out.println(rule1.getClass());
Rule rule2 = rule1.toRule();
System.out.println(rule2.getClass());
return rule2;
)
.collect(Collectors.toList());
String dataId = genDataId(app, postfix);
/**
* 俩种存储只是入参不同,为了满足功能的实现,存入nacos后,会有俩个配置,以后继续完善
*/
// 存储,控制微服务使用,即可以起到拦截作用,但是由于无法显示到控制台
configService.publishConfig(
dataId,
NacosConfigUtil.GROUP_ID,
JSONUtils.toJSONString(ruleForApp)
);
// 存储,给控制台显示使用,由于数据太多,会出现转化异常,虽然可以提供控制台显示,但是无法对微服务进行保护
configService.publishConfig(
dataId + DASHBOARD_POSTFIX,
NacosConfigUtil.GROUP_ID,
JSONUtils.toJSONString(rules)
);
/**
*
* 从Nacos server中查询响应规则,并将其反序列化成对应Rule实体
*
* @param configService nacos config service
* @param appName 应用名称
* @param postfix 规则后缀 eg.NacosConfigUtil.FLOW_DATA_ID_POSTFIX
* @param clazz 类
* @param <T> 泛型
* @return 规则对象列表
* @throws NacosException 异常
*/
public static <T> List<T> getRuleEntitiesFromNacos(ConfigService configService, String appName, String postfix, Class<T> clazz) throws NacosException
String rules = configService.getConfig(
genDataId(appName, postfix) + DASHBOARD_POSTFIX,
NacosConfigUtil.GROUP_ID,
3000
);
if (StringUtil.isEmpty(rules))
return new ArrayList<>();
return JSONUtils.parseObject(clazz, rules);
private static String genDataId(String appName, String postfix)
return appName + postfix;
AuthorityRuleNacosProvider
@Component("authorityRuleNacosProvider")
public class AuthorityRuleNacosProvider implements DynamicRuleProvider<List<AuthorityRuleEntity>>
@Autowired
private ConfigService configService;
@Override
public List<AuthorityRuleEntity> getRules(String appName) throws Exception
return NacosConfigUtil.getRuleEntitiesFromNacos(
this.configService,
appName,
NacosConfigUtil.AUTHORITY_DATA_ID_POSTFIX,
AuthorityRuleEntity.class
);
AuthorityRuleNacosPublisher
@Component("authorityRuleNacosPublisher")
public class AuthorityRuleNacosPublisher implements DynamicRulePublisher<List<AuthorityRuleEntity>>
@Autowired
private ConfigService configService;
@Override
public void publish(String app, List<AuthorityRuleEntity> rules) throws Exception
NacosConfigUtil.setRuleStringToNacos(
this.configService,
app,
NacosConfigUtil.AUTHORITY_DATA_ID_POSTFIX,
rules
);
DegradeRuleNacosProvider
@Component("degradeRuleNacosProvider")
public class DegradeRuleNacosProvider implements DynamicRuleProvider<List<DegradeRuleEntity>>
@Autowired
private ConfigService configService;
@Override
public List<DegradeRuleEntity> getRules(String appName) throws Exception
return NacosConfigUtil.getRuleEntitiesFromNacos(
this.configService,
appName,
NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,
DegradeRuleEntity.class
);
DegradeRuleNacosPublisher
@Component("degradeRuleNacosPublisher")
public class DegradeRuleNacosPublisher implements DynamicRulePublisher<List<DegradeRuleEntity>>
@Autowired
private ConfigService configService;
@Override
public void publish(String app, List<DegradeRuleEntity> rules) throws Exception
NacosConfigUtil.setRuleStringToNacos(
this.configService,
app,
NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,
rules
);
FlowRuleNacosProvider
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>>
@Autowired
private ConfigService configService;
@Override
public List<FlowRuleEntity> getRules(String appName) throws Exception
return NacosConfigUtil.getRuleEntitiesFromNacos(
this.configService,
appName,
NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
FlowRuleEntity.class
);
FlowRuleNacosPublisher
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>>
@Autowired
private ConfigService configService;
@Override
public void publish(String app, List<FlowRuleEntity> rules) throws Exception
NacosConfigUtil.setRuleStringToNacos(
this.configService,
app,
NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
rules
);
ParamFlowRuleNacosProvider
@Component("paramFlowRuleNacosProvider")
public class ParamFlowRuleNacosProvider implements DynamicRuleProvider<List<ParamFlowRuleEntity>>
@Autowired
private ConfigService configService;
@Override
public List<ParamFlowRuleEntity> getRules(String appName) throws Exception
return NacosConfigUtil.getRuleEntitiesFromNacos(
this.configService,
appName,
NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,
ParamFlowRuleEntity.class
);
ParamFlowRuleNacosPublisher
@Component("paramFlowRuleNacosPublisher")
public class ParamFlowRuleNacosPublisher implements DynamicRulePublisher<List<ParamFlowRuleEntity>>
@Autowired
private ConfigService configService;
@Override
public void publish(String app, List<ParamFlowRuleEntity> rules) throws Exception
NacosConfigUtil.setRuleStringToNacos(
this.configService,
app,
NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,
rules
);
SystemRuleNacosProvider
@Component("systemRuleNacosProvider")
public class SystemRuleNacosProvider implements DynamicRuleProvider<List<SystemRuleEntity>>
@Autowired
private ConfigService configService;
@Override
public List<SystemRuleEntity> getRules(String appName) throws Exception
return NacosConfigUtil.getRuleEntitiesFromNacos(
this.configService,
appName,
NacosConfigUtil.SYSTEM_DATA_ID_POSTFIX,
SystemRuleEntity.class
);
SystemRuleNacosPublisher
@Component("systemRuleNacosPublisher")
public class SystemRuleNacosPublisher implements DynamicRulePublisher<List<SystemRuleEntity>>
@Autowired
private ConfigService configService;
@Override
public void publish(String app, List<SystemRuleEntity> rules) throws Exception
NacosConfigUtil.setRuleStringToNacos(
this.configService,
app,
NacosConfigUtil.SYSTEM_DATA_ID_POSTFIX,
rules
);
*修改5种规则crud的controller层 主要修改数据的存储,将原来存入内存的规则,现在存入nacos,并将nacos的数据push到sentinel的数据源中。
AuthorityRuleController
@RestController
@RequestMapping(value = "/authority")
public class AuthorityRuleController
private final Logger logger = LoggerFactory.getLogger(AuthorityRuleController.class);
@Autowired
@Qualifier("authorityRuleNacosProvider")
private DynamicRuleProvider<List<AuthorityRuleEntity>> ruleProvider;
@Autowired
@Qualifier("authorityRuleNacosPublisher")
private DynamicRulePublisher<List<AuthorityRuleEntity>> rulePublisher;
/* @Autowired
private SentinelApiClient sentinelApiClient;*/
@Autowired
private RuleRepository<AuthorityRuleEntity, Long> repository;
@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");
if (port == null || port <= 0)
return Result.ofFail(-1, "Invalid parameter: port");
try
// List<AuthorityRuleEntity> rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port);
List<AuthorityRuleEntity> rules = ruleProvider.getRules(app);
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
catch (Throwable throwable)
logger.error("Error when querying authority rules", throwable);
return Result.ofFail(-1, throwable.getMessage());
private <R> Result<R> checkEntityInternal(AuthorityRuleEntity entity)
if (entity == null)
return Result.ofFail(-1, "bad rule body");
if (StringUtil.isBlank(entity.getApp()))
return Result.ofFail(-1, "app can't be null or empty");
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, "port can't be null");
if (entity.getRule() == null)
return Result.ofFail(-1, "rule can't be null");
if (StringUtil.isBlank(entity.getResource()))
return Result.ofFail(-1, "resource name cannot be null or empty");
if (StringUtil.isBlank(entity.getLimitApp()))
return Result.ofFail(-1, "limitApp should be valid");
if (entity.getStrategy() != RuleConstant.AUTHORITY_WHITE
&& entity.getStrategy() != RuleConstant.AUTHORITY_BLACK)
return Result.ofFail(-1, "Unknown strategy (must be blacklist or whitelist)");
return null;
@PostMapping("/rule")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<AuthorityRuleEntity> apiAddAuthorityRule(@RequestBody AuthorityRuleEntity entity)
Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null)
return checkResult;
entity.setId(null);
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
try
entity = repository.save(entity);
publishRules(entity.getApp());
catch (Throwable throwable)
logger.error("Failed to add flow rule", throwable);
return Result.ofThrowable(-1, throwable);
return Result.ofSuccess(entity);
@PutMapping("/rule/id")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<AuthorityRuleEntity> apiUpdateParamFlowRule(@PathVariable("id") Long id,
@RequestBody AuthorityRuleEntity entity)
if (id == null || id <= 0)
return Result.ofFail(-1, "Invalid id");
Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null)
return checkResult;
entity.setId(id);
Date date = new Date();
entity.setGmtCreate(null);
entity.setGmtModified(date);
try
entity = repository.save(entity);
if (entity == null)
return Result.ofFail(-1, "Failed to save authority rule");
publishRules(entity.getApp());
catch (Throwable throwable)
logger.error("Failed to save authority rule", throwable);
return Result.ofThrowable(-1, throwable);
return Result.ofSuccess(entity);
@DeleteMapping("/rule/id")
@AuthAction(PrivilegeType.DELETE_RULE)
public Result<Long> apiDeleteRule(@PathVariable("id") Long id)
if (id == null || id <= 0)
return Result.ofFail(-1, "Invalid id");
AuthorityRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null)
return Result.ofSuccess(null);
try
repository.delete(id);
publishRules(oldEntity.getApp());
catch (Exception e)
return Result.ofFail(-1, e.getMessage());
return Result.ofSuccess(id);
/* private boolean publishRules(String app, String ip, Integer port)
List<AuthorityRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setAuthorityRuleOfMachine(app, ip, port, rules);
*/
private void publishRules(String app) throws Exception
List<AuthorityRuleEntity> rules = repository.findAllByApp(app);
rulePublisher.publish(app, rules);
DegradeController
@Controller
@RequestMapping(value = "/degrade", produces = MediaType.APPLICATION_JSON_VALUE)
public class DegradeController
private final Logger logger = LoggerFactory.getLogger(DegradeController.class);
@Autowired
private InMemDegradeRuleStore repository;
@Autowired
@Qualifier("degradeRuleNacosProvider")
private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;
@Autowired
@Qualifier("degradeRuleNacosPublisher")
private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;
/*@Autowired
private SentinelApiClient sentinelApiClient;*/
@ResponseBody
@RequestMapping("/rules.json")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<DegradeRuleEntity>> queryMachineRules(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 = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port);
//去nacos中取数据
List<DegradeRuleEntity> rules = ruleProvider.getRules(app);
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
catch (Throwable throwable)
logger.error("queryApps error:", throwable);
return Result.ofThrowable(-1, throwable);
@ResponseBody
@RequestMapping("/new.json")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<DegradeRuleEntity> add(String app, String ip, Integer port, String limitApp, String resource,
Double count, Integer timeWindow, Integer grade)
if (StringUtil.isBlank(app))
return Result.ofFail(-1, "app can't be null or empty");
if (StringUtil.isBlank(ip))
return Result.ofFail(-1, "ip can't be null or empty");
if (port == null)
return Result.ofFail(-1, "port can't be null");
if (StringUtil.isBlank(limitApp))
return Result.ofFail(-1, "limitApp can't be null or empty");
if (StringUtil.isBlank(resource))
return Result.ofFail(-1, "resource can't be null or empty");
if (count == null)
return Result.ofFail(-1, "count can't be null");
if (timeWindow == null)
return Result.ofFail(-1, "timeWindow can't be null");
if (grade == null)
return Result.ofFail(-1, "grade can't be null");
if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT)
return Result.ofFail(-1, "Invalid grade: " + grade);
DegradeRuleEntity entity = new DegradeRuleEntity();
entity.setApp(app.trim());
entity.setIp(ip.trim());
entity.setPort(port);
entity.setLimitApp(limitApp.trim());
entity.setResource(resource.trim());
entity.setCount(count);
entity.setTimeWindow(timeWindow);
entity.setGrade(grade);
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
try
entity = repository.save(entity);
//推送信息
publishRules(app);
catch (Throwable throwable)
logger.error("add error:", throwable);
return Result.ofThrowable(-1, throwable);
/* if (!publishRules(app, ip, port))
logger.info("publish degrade rules fail after rule add");
*/
return Result.ofSuccess(entity);
@ResponseBody
@RequestMapping("/save.json")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<DegradeRuleEntity> updateIfNotNull(Long id, String app, String limitApp, String resource,
Double count, Integer timeWindow, Integer grade)
if (id == null)
return Result.ofFail(-1, "id can't be null");
if (grade != null)
if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT)
return Result.ofFail(-1, "Invalid grade: " + grade);
DegradeRuleEntity entity = repository.findById(id);
if (entity == null)
return Result.ofFail(-1, "id " + id + " dose not exist");
if (StringUtil.isNotBlank(app))
entity.setApp(app.trim());
if (StringUtil.isNotBlank(limitApp))
entity.setLimitApp(limitApp.trim());
if (StringUtil.isNotBlank(resource))
entity.setResource(resource.trim());
if (count != null)
entity.setCount(count);
if (timeWindow != null)
entity.setTimeWindow(timeWindow);
if (grade != null)
entity.setGrade(grade);
Date date = new Date();
entity.setGmtModified(date);
try
entity = repository.save(entity);
//推送规则
publishRules(entity.getApp());
catch (Throwable throwable)
logger.error("save error:", throwable);
return Result.ofThrowable(-1, throwable);
/* if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort()))
logger.info("publish degrade rules fail after rule update");
*/
return Result.ofSuccess(entity);
@ResponseBody
@RequestMapping("/delete.json")
@AuthAction(PrivilegeType.DELETE_RULE)
public Result<Long> delete(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("delete error:", throwable);
return Result.ofThrowable(-1, throwable);
/* if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()))
logger.info("publish degrade rules fail after rule delete");
*/
return Result.ofSuccess(id);
/* private boolean publishRules(String app, String ip, Integer port)
List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);
*/
private void publishRules(String app) throws Exception
List<DegradeRuleEntity> rules = repository.findAllByApp(app);
rulePublisher.publish(app, rules);
FlowControllerV1
@RestController
@RequestMapping(value = "/v1/flow")
public class FlowControllerV1
private final Logger logger = LoggerFactory.getLogger(FlowControllerV1.class);
@Autowired
private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;
/* @Autowired
private SentinelApiClient sentinelApiClient;*/
@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
@GetMapping("/rules")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<FlowRuleEntity>> apiQueryMachineRules( @RequestParam String app)
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);
List<FlowRuleEntity> rules = ruleProvider.getRules(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);
private <R> Result<R> checkEntityInternal(FlowRuleEntity entity)
if (entity == null)
return Result.ofFail(-1, "invalid body");
if (StringUtil.isBlank(entity.getApp()))
return Result.ofFail(-1, "app can't be null or empty");
if (StringUtil.isBlank(entity.getIp()))
return Result.ofFail(-1, "ip can't be null or empty");
if (entity.getPort() == null)
return Result.ofFail(-1, "port can't be null");
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");
if (entity.getGrade() == null)
return Result.ofFail(-1, "grade can't be null");
if (entity.getGrade() != 0 && entity.getGrade() != 1)
return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got");
if (entity.getCount() == null || entity.getCount() < 0)
return Result.ofFail(-1, "count should be at lease zero");
if (entity.getStrategy() == null)
return Result.ofFail(-1, "strategy can't be null");
if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource()))
return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0");
if (entity.getControlBehavior() == null)
return Result.ofFail(-1, "controlBehavior can't be null");
int controlBehavior = entity.getControlBehavior();
if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null)
return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1");
if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null)
return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2");
if (entity.isClusterMode() && entity.getClusterConfig() == null)
return Result.ofFail(-1, "cluster config should be valid");
return null;
@PostMapping("/rule")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<FlowRuleEntity> apiAddFlowRule(@RequestBody FlowRuleEntity entity)
Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null)
return checkResult;
entity.setId(null);
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
entity.setLimitApp(entity.getLimitApp().trim());
entity.setResource(entity.getResource().trim());
try
entity = repository.save(entity);
publishRules(entity.getApp());
catch (Throwable throwable)
logger.error("Failed to add flow rule", throwable);
return Result.ofThrowable(-1, throwable);
return Result.ofSuccess(entity);
@PutMapping("/save.json")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<FlowRuleEntity> apiUpdateFlowRule(@PathVariable("id") Long id,
@RequestBody FlowRuleEntity entity)
if (id == null || id <= 0)
return Result.ofFail(-1, "Invalid id");
FlowRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null)
return Result.ofFail(-1, "id " + id + " does not exist");
if (entity == null)
return Result.ofFail(-1, "invalid body");
entity.setApp(oldEntity.getApp());
entity.setIp(oldEntity.getIp());
entity.setPort(oldEntity.getPort());
Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null)
return checkResult;
entity.setId(id);
Date date = new Date();
entity.setGmtCreate(oldEntity.getGmtCreate());
entity.setGmtModified(date);
try
entity = repository.save(entity);
if (entity == null)
return Result.ofFail(-1, "save entity fail");
publishRules(oldEntity.getApp());
catch (Throwable throwable)
logger.error("Failed to update flow rule", throwable);
return Result.ofThrowable(-1, throwable);
return Result.ofSuccess(entity);
@DeleteMapping("/delete.json")
@AuthAction(PrivilegeType.DELETE_RULE)
public Result<Long> apiDeleteFlowRule(Long id)
if (id == null || id <= 0)
return Result.ofFail(-1, "Invalid id");
FlowRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null)
return Result.ofSuccess(null);
try
repository.delete(id);
publishRules(oldEntity.getApp());
catch (Exception e)
return Result.ofFail(-1, e.getMessage());
return Result.ofSuccess(id);
/* private CompletableFuture<Void> publishRules(String app, String ip, Integer port)
List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules);
*/
private void publishRules(/*@NonNull*/ String app) throws Exception
List<FlowRuleEntity> rules = repository.findAllByApp(app);
rulePublisher.publish(app, rules);
ParamFlowRuleController
@RestController
@RequestMapping(value = "/paramFlow")
public class ParamFlowRuleController
private final Logger logger = LoggerFactory.getLogger(ParamFlowRuleController.class);
/* @Autowired
private SentinelApiClient sentinelApiClient;*/
@Autowired
@Qualifier("paramFlowRuleNacosProvider")
private DynamicRuleProvider<List<ParamFlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("paramFlowRuleNacosPublisher")
private DynamicRulePublisher<List<ParamFlowRuleEntity>> rulePublisher;
@Autowired
private AppManagement appManagement;
@Autowired
private RuleRepository<ParamFlowRuleEntity, Long> repository;
private boolean checkIfSupported(String app, String ip, int port)
try
return Optional.ofNullable(appManagement.getDetailApp(app))
.flatMap(e -> e.getMachine(ip, port))
.flatMap(m -> VersionUtils.parseVersion(m.getVersion())
.map(v -> v.greaterOrEqual(version020)))
.orElse(true);
// If error occurred or cannot retrieve machine info, return true.
catch (Exception ex)
return true;
@GetMapping("/rules")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<ParamFlowRuleEntity>> 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");
if (port == null || port <= 0)
return Result.ofFail(-1, "Invalid parameter: port");
if (!checkIfSupported(app, ip, port))
return unsupportedVersion();
try
/* return sentinelApiClient.fetchParamFlowRulesOfMachine(app, ip, port)
.thenApply(repository::saveAll)
.thenApply(Result::ofSuccess)
.get();*/
List<ParamFlowRuleEntity> rules = ruleProvider.getRules(app);
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
catch (ExecutionException ex)
logger.error("Error when querying parameter flow rules", ex.getCause());
if (isNotSupported(ex.getCause()))
return unsupportedVersion();
else
return Result.ofThrowable(-1, ex.getCause());
catch (Throwable throwable)
logger.error("Error when querying parameter flow rules", throwable);
return Result.ofFail(-1, throwable.getMessage());
private boolean isNotSupported(Throwable ex)
return ex instanceof CommandNotFoundException;
@PostMapping("/rule")
@AuthAction(AuthService.PrivilegeType.WRITE_RULE)
public Result<ParamFlowRuleEntity> apiAddParamFlowRule(@RequestBody ParamFlowRuleEntity entity)
Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null)
return checkResult;
if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort()))
return unsupportedVersion();
entity.setId(null);
entity.getRule().setResource(entity.getResource().trim());
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
try
entity = repository.save(entity);
// publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get();
publishRules(entity.getApp());
return Result.ofSuccess(entity);
catch (ExecutionException ex)
logger.error("Error when adding new parameter flow rules", ex.getCause());
if (isNotSupported(ex.getCause()))
return unsupportedVersion();
else
return Result.ofThrowable(-1, ex.getCause());
catch (Throwable throwable)
logger.error("Error when adding new parameter flow rules", throwable);
return Result.ofFail(-1, throwable.getMessage());
private <R> Result<R> checkEntityInternal(ParamFlowRuleEntity entity)
if (entity == null)
return Result.ofFail(-1, "bad rule body");
if (StringUtil.isBlank(entity.getApp()))
return Result.ofFail(-1, "app can't be null or empty");
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, "port can't be null");
if (entity.getRule() == null)
return Result.ofFail(-1, "rule can't be null");
if (StringUtil.isBlank(entity.getResource()))
return Result.ofFail(-1, "resource name cannot be null or empty");
if (entity.getCount() < 0)
return Result.ofFail(-1, "count should be valid");
if (entity.getGrade() != RuleConstant.FLOW_GRADE_QPS)
return Result.ofFail(-1, "Unknown mode (blockGrade) for parameter flow control");
if (entity.getParamIdx() == null || entity.getParamIdx() < 0)
return Result.ofFail(-1, "paramIdx should be valid");
if (entity.getDurationInSec() <= 0)
return Result.ofFail(-1, "durationInSec should be valid");
if (entity.getControlBehavior() < 0)
return Result.ofFail(-1, "controlBehavior should be valid");
return null;
@PutMapping("/rule/id")
@AuthAction(AuthService.PrivilegeType.WRITE_RULE)
public Result<ParamFlowRuleEntity> apiUpdateParamFlowRule(@PathVariable("id") Long id,
@RequestBody ParamFlowRuleEntity entity)
if (id == null || id <= 0)
return Result.ofFail(-1, "Invalid id");
ParamFlowRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null)
return Result.ofFail(-1, "id " + id + " does not exist");
Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null)
return checkResult;
if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort()))
return unsupportedVersion();
entity.setId(id);
Date date = new Date();
entity.setGmtCreate(oldEntity.getGmtCreate());
entity.setGmtModified(date);
try
entity = repository.save(entity);
// publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get();
publishRules(entity.getApp());
return Result.ofSuccess(entity);
catch (ExecutionException ex)
logger.error("Error when updating parameter flow rules, id=" + id, ex.getCause());
if (isNotSupported(ex.getCause()))
return unsupportedVersion();
else
return Result.ofThrowable(-1, ex.getCause());
catch (Throwable throwable)
logger.error("Error when updating parameter flow rules, id=" + id, throwable);
return Result.ofFail(-1, throwable.getMessage());
@DeleteMapping("/rule/id")
@AuthAction(PrivilegeType.DELETE_RULE)
public Result<Long> apiDeleteRule(@PathVariable("id") Long id)
if (id == null)
return Result.ofFail(-1, "id cannot be null");
ParamFlowRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null)
return Result.ofSuccess(null);
try
repository.delete(id);
// publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get();
publishRules(oldEntity.getApp());
return Result.ofSuccess(id);
catch (ExecutionException ex)
logger.error("Error when deleting parameter flow rules", ex.getCause());
if (isNotSupported(ex.getCause()))
return unsupportedVersion();
else
return Result.ofThrowable(-1, ex.getCause());
catch (Throwable throwable)
logger.error("Error when deleting parameter flow rules", throwable);
return Result.ofFail(-1, throwable.getMessage());
/* private CompletableFuture<Void> publishRules(String app, String ip, Integer port)
List<ParamFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setParamFlowRuleOfMachine(app, ip, port, rules);
*/
private void publishRules(String app) throws Exception
List<ParamFlowRuleEntity> rules = repository.findAllByApp(app);
rulePublisher.publish(app, rules);
private <R> Result<R> unsupportedVersion()
return Result.ofFail(4041,
"Sentinel client not supported for parameter flow control (unsupported version or dependency absent)");
private final SentinelVersion version020 = new SentinelVersion().setMinorVersion(2);
SystemController
@RestController
@RequestMapping("/system")
public class SystemController
private final Logger logger = LoggerFactory.getLogger(SystemController.class);
@Autowired
private RuleRepository<SystemRuleEntity, Long> repository;
@Autowired
@Qualifier("systemRuleNacosProvider")
private DynamicRuleProvider<List<SystemRuleEntity>> ruleProvider;
@Autowired
@Qualifier("systemRuleNacosPublisher")
private DynamicRulePublisher<List<SystemRuleEntity>> rulePublisher;
/* @Autowired
private SentinelApiClient sentinelApiClient;*/
private <R> Result<R> checkBasicParams(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");
if (port <= 0 || port > 65535)
return Result.ofFail(-1, "port should be in (0, 65535)");
return null;
@GetMapping("/rules.json")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<SystemRuleEntity>> apiQueryMachineRules(String app, String ip,
Integer port)
Result<List<SystemRuleEntity>> checkResult = checkBasicParams(app, ip, port);
if (checkResult != null)
return checkResult;
try
// List<SystemRuleEntity> rules = sentinelApiClient.fetchSystemRuleOfMachine(app, ip, port);
List<SystemRuleEntity> rules = ruleProvider.getRules(app);
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
catch (Throwable throwable)
logger.error("Query machine system rules error", throwable);
return Result.ofThrowable(-1, throwable);
private int countNotNullAndNotNegative(Number... values)
int notNullCount = 0;
for (int i = 0; i < values.length; i++)
if (values[i] != null && values[i].doubleValue() >= 0)
notNullCount++;
return notNullCount;
@RequestMapping("/new.json")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<SystemRuleEntity> apiAdd(String app, String ip, Integer port,
Double highestSystemLoad, Double highestCpuUsage, Long avgRt,
Long maxThread, Double qps)
Result<SystemRuleEntity> checkResult = checkBasicParams(app, ip, port);
if (checkResult != null)
return checkResult;
int notNullCount = countNotNullAndNotNegative(highestSystemLoad, avgRt, maxThread, qps, highestCpuUsage);
if (notNullCount != 1)
return Result.ofFail(-1, "only one of [highestSystemLoad, avgRt, maxThread, qps,highestCpuUsage] "
+ "value must be set > 0, but " + notNullCount + " values get");
if (null != highestCpuUsage && highestCpuUsage > 1)
return Result.ofFail(-1, "highestCpuUsage must between [0.0, 1.0]");
SystemRuleEntity entity = new SystemRuleEntity();
entity.setApp(app.trim());
entity.setIp(ip.trim());
entity.setPort(port);
// -1 is a fake value
if (null != highestSystemLoad)
entity.setHighestSystemLoad(highestSystemLoad);
else
entity.setHighestSystemLoad(-1D);
if (null != highestCpuUsage)
entity.setHighestCpuUsage(highestCpuUsage);
else
entity.setHighestCpuUsage(-1D);
if (avgRt != null)
entity.setAvgRt(avgRt);
else
entity.setAvgRt(-1L);
if (maxThread != null)
entity.setMaxThread(maxThread);
else
entity.setMaxThread(-1L);
if (qps != null)
entity.setQps(qps);
else
entity.setQps(-1D);
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
try
entity = repository.save(entity);
//推送
publishRules(app);
catch (Throwable throwable)
logger.error("Add SystemRule error", throwable);
return Result.ofThrowable(-1, throwable);
/* if (!publishRules(app, ip, port))
logger.warn("Publish system rules fail after rule add");
*/
return Result.ofSuccess(entity);
@GetMapping("/save.json")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<SystemRuleEntity> apiUpdateIfNotNull(Long id, String app, Double highestSystemLoad,
Double highestCpuUsage, Long avgRt, Long maxThread, Double qps)
if (id == null)
return Result.ofFail(-1, "id can't be null");
SystemRuleEntity entity = repository.findById(id);
if (entity == null)
return Result.ofFail(-1, "id " + id + " dose not exist");
if (StringUtil.isNotBlank(app))
entity.setApp(app.trim());
if (highestSystemLoad != null)
if (highestSystemLoad < 0)
return Result.ofFail(-1, "highestSystemLoad must >= 0");
entity.setHighestSystemLoad(highestSystemLoad);
if (highestCpuUsage != null)
if (highestCpuUsage < 0)
return Result.ofFail(-1, "highestCpuUsage must >= 0");
if (highestCpuUsage > 1)
return Result.ofFail(-1, "highestCpuUsage must <= 1");
entity.setHighestCpuUsage(highestCpuUsage);
if (avgRt != null)
if (avgRt < 0)
return Result.ofFail(-1, "avgRt must >= 0");
entity.setAvgRt(avgRt);
if (maxThread != null)
if (maxThread < 0)
return Result.ofFail(-1, "maxThread must >= 0");
entity.setMaxThread(maxThread);
if (qps != null)
if (qps < 0)
return Result.ofFail(-1, "qps must >= 0");
entity.setQps(qps);
Date date = new Date();
entity.setGmtModified(date);
try
entity = repository.save(entity);
//推送
publishRules(entity.getApp());
catch (Throwable throwable)
logger.error("save error:", throwable);
return Result.ofThrowable(-1, throwable);
/* if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort()))
logger.info("publish system rules fail after rule update");
*/
return Result.ofSuccess(entity);
@RequestMapping("/delete.json")
@AuthAction(PrivilegeType.DELETE_RULE)
public Result<?> delete(Long id)
if (id == null)
return Result.ofFail(-1, "id can't be null");
SystemRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null)
return Result.ofSuccess(null);
try
repository.delete(id);
//推送
publishRules(oldEntity.getApp());
catch (Throwable throwable)
logger.error("delete error:", throwable);
return Result.ofThrowable(-1, throwable);
/* if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()))
logger.info("publish system rules fail after rule delete");
*/
return Result.ofSuccess(id);
/* private boolean publishRules(String app, String ip, Integer port)
List<SystemRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setSystemRuleOfMachine(app, ip, port, rules);
*/
private void publishRules(String app) throws Exception
List<SystemRuleEntity> rules = repository.findAllByApp(app);
rulePublisher.publish(app, rules);
控制台改造完成
Sentinel中加入规则
SpringCloud系列——流量控制组件Sentinel实战
前言
在微服务应用中,保证服务和服务之间的稳定性是至关重要的。目前在springcloud技术栈中,最常见的俩种服务保护组件就是Netflix公司的hytrix和阿里巴巴出品的Sentinel。本节我们主要介绍一下Sentinel这款功能强大的精品组件。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。看到这一段官方描述我们大概就对Sentinel组件能够干什么,有了一个初步的认识,具体的官方文档说明,请参考官方地址:https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5,话不多说,开始我们的sentinel开发实战。
正文
- 安装sentinel客户端
①下载sentinel客户端
地址:https://github.com/alibaba/Sentinel/releases
②安装sentinel客户端命令:java -Dserver.port=8081 -Dcsp.sentinel.dashboard.server=localhost:8081 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar -Dcsp.sentinel.app.type=1
③登录sentinel客户端,默认用户名和密码都是sentinel
- 微服务集成sentinel
说明:使用nacos作为sentinel相关规则配置数据的持久化存储中心,在网关层实现服务的限流、降级等操作,分别通过router和API俩种方式实现微服务限流的实战案例。
①引入 sentinel的pom依赖
<!--sentinel配置数据源nacos--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
② 引入gateway网关整合sentinel的相关pom依赖
<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-alibaba-sentinel-gateway --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> <version>2.2.5.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-gateway --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <version>2.2.5.RELEASE</version> </dependency>
③配置网关的相关sentinel配置
spring: cloud: gateway: routes: - id: xxx_manager_router #路由ID uri: lb://xxx-manager #负载均衡到转发的微服务地址 predicates: - Path=/api/manager/** #路由断言,以/api/manager/开头的访问请求使用该路由策略转发,转发到上面配置的微服务 filters: - RewritePath=/api/manager/(?<segment>.*),/$\\{segment} #路径改写,将网关的请求地址改写为真实的微服务地址 #sentinel配置 sentinel: transport: dashboard: localhost:8081 port: 8719 filter: enabled: true scg: #配置限流之后,响应内容 fallback: mode: response response-status: 200 response-body: "服务繁忙,请稍后重试!" content-type: "application/json" #数据源配置 datasource: gw-router-group: nacos: server-addr: ${spring.cloud.nacos.server-addr} dataId: ${spring.application.name}-gw-router-group-flow-rules groupid: DEFAULT_GROUP data-type: json rule-type: gw-flow gw-api-group: nacos: server-addr: ${spring.cloud.nacos.server-addr} dataId: ${spring.application.name}-gw-api-group-flow-rules groupid: DEFAULT_GROUP data-type: json rule-type: gw-api-group
④在nacos配置限流策略
(1)xxx-gw-router-group-flow-rules配置
[ { "resource": "xxx_manager_router", "limitApp": "default", "grade": 1, "count":12, "strategy": 0, "controlBehavior": 0, "clusterMode": false }, { "resource": "xxx_manager_api", "count": 2, "grade": 1, "resourceMode": 1 } ]
(2)xxx-gw-api-group-flow-rules配置
[ { "apiName": "xxx_manager_api", "predicateItems": [ { "pattern": "/api/manager/sysCode/*", "matchStrategy": 1 } ] } ]
说明:具体的配置参数说明请参考官网地址https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81
⑤测试验证
说明:通过网关访问以/api/manager/sysCode/*的请求,可以看到,频繁调用,会出现配置限流的提示,这里的API限流配置的是每秒钟2次就会限流,router限流配置是每秒钟12次就会限流,可以通过修改nacos配置中心的count参数动态修改限流的策略
⑥sentinel控制台可以查看流控配置以及具体的访问请求流量等信息
结语
ok,到这里关于Sentinel的应用实战就结束了,我们下期见。。。
以上是关于在springCloud架构下实现sentinel持久化到nacos的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloud Alibaba :Sentinel 流量控制组件
SpringCloud Alibaba Sentinel 服务限流熔断降级 - 基于dashboard方式配制
SpringCloud Alibaba Sentinel实现熔断与限流