在springCloud架构下实现sentinel持久化到nacos

Posted liuec1002

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在springCloud架构下实现sentinel持久化到nacos相关的知识,希望对你有一定的参考价值。

文章目录

前言

一: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框架都有啥啊

SpringCloud Alibaba :Sentinel 流量控制组件

SpringCloud Alibaba Sentinel 服务限流熔断降级 - 基于dashboard方式配制

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel 流量控制 - 程序配制方式实现

#yyds干货盘点# springcloud整合Sentinel使用Nacos存储规则