Sentinel规则持久化到Nacos及规则数据双向同步

Sentinel规则持久化到Nacos及规则数据双向同步

说明:此处Sentinel网关规则持久化是使用推模式扩展,将Sentinel网关规则持久化到Nacos,至于持久化到ZooKeeper, Redis, Apollo, etcd,可以根据具体数据源进行扩展,这里就不再说明

1、前置工作

1.1、版本说明

Spring Cloud Alibaba Version:2.2.7.RELEASE
Sentinel Version:1.8.1

关于SpringcloudAlibaba相关组件版本,可参考官网版本说明:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

1.2、源码下载

源码地址如下,点击Tags选择版本为1.8.1,然后点击下载ZIP压缩包,下载地址如下:

https://github.com/alibaba/Sentinel/tree/1.8.1

1.3、解压并打开项目

将压缩包解压,用IDE工具打开sentinel-dashboard项目,由于是将Sentinel控制台定义的规则持久化到Nacos,所以,只需要打开sentinel-dashboard项目即可

2、sentinel-dashboard中网关规则持久化改造

2.1、pom依赖修改和导入

<!-- 找到Sentinel持久化到Nacos数据源,将scope注释掉,如下所示 -->
<!-- for Nacos rule publisher sample -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <!--<scope>test</scope>-->
</dependency>

<!-- 导入下面依赖, 方便后续改造使用 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.11</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.22</version>
</dependency>

<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-client</artifactId>
    <version>2.0.3</version>
</dependency>

2.2、添加nacos配置

在src/main/resources/application.properties配置文件中添加nacos配置,如下所示

# nacos settings
# nacos地址根据项目中nacos服务的地址配置
nacos.address=127.0.0.1:8848
# 如果有单独的命名空间添加此配置, 没有就不设置
nacos.namespace=35788967-0687-4f5e-a53e-88fe25e59266
nacos.username=nacos
nacos.password=nacos

说明:如果不习惯配置文件为properties格式,也可以修改为yml格式,这里仅为举例使用

2.3、创建Nacos配置类

复制 src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos 下的NacosConfig、NacosConfigUtil类到src/main/java/com/alibaba/csp/sentinel/dashboard/rule/nacos 下,如下图所示

2.4、修改Nacos配置类

2.4.1、修改NacosConfig类

NacosConfig配置类修改如下,主要是添加规则和创建Nacos配置服务,NacosConfig配置类修改后代码如下

package com.alibaba.csp.sentinel.dashboard.rule.nacos;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.*;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;
import java.util.Properties;

/**
 * @author 星空流年
 */
@Configuration
public class NacosConfig {

    @Value("${nacos.address}")
    private String address;
    
    @Value("${nacos.namespace}")
    private String namespace;
    
    @Value("${nacos.username}")
    private String username;
    
    @Value("${nacos.password}")
    private String password;

    /**
     * 流控规则
     *
     * @return
     */
    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    /**
     * 授权规则
     *
     * @return
     */
    @Bean
    public Converter<List<AuthorityRuleEntity>, String> authorRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<AuthorityRuleEntity>> authorRuleEntityDecoder() {
        return s -> JSON.parseArray(s, AuthorityRuleEntity.class);
    }

    /**
     * 降级规则
     *
     * @return
     */
    @Bean
    public Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {
        return s -> JSON.parseArray(s, DegradeRuleEntity.class);
    }

    /**
     * 热点规则
     *
     * @return
     */
    @Bean
    public Converter<List<ParamFlowRuleEntity>, String> paramRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<ParamFlowRuleEntity>> paramRuleEntityDecoder() {
        return s -> JSON.parseArray(s, ParamFlowRuleEntity.class);
    }

    /**
     * 系统规则
     *
     * @return
     */
    @Bean
    public Converter<List<SystemRuleEntity>, String> systemRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<SystemRuleEntity>> systemRuleEntityDecoder() {
        return s -> JSON.parseArray(s, SystemRuleEntity.class);
    }

    /**
     * 网关API分组管理规则
     *
     * @return
     * @throws Exception
     */
    @Bean
    public Converter<List<ApiDefinitionEntity>, String> apiDefinitionEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<ApiDefinitionEntity>> apiDefinitionEntityDecoder() {
        return s -> JSON.parseArray(s, ApiDefinitionEntity.class);
    }

    /**
     * 网关流控规则
     *
     * @return
     * @throws Exception
     */
    @Bean
    public Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<GatewayFlowRuleEntity>> gatewayFlowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, GatewayFlowRuleEntity.class);
    }

    /**
     * nacos配置服务
     * 
     * @return
     * @throws Exception
     */
    @Bean
    public ConfigService nacosConfigService() throws Exception {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, address);
        properties.put(PropertyKeyConst.NAMESPACE, namespace);
        properties.put(PropertyKeyConst.USERNAME, username);
        properties.put(PropertyKeyConst.PASSWORD, password);
        return ConfigFactory.createConfigService(properties);
    }
}

2.4.2、修改NacosConfigUtil类

NacosConfigUtil工具类修改如下,主要是添加规则常量,添加内容如下

/** 流控规则 */
public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";
/** 系统保护规则 */
public static final String SYSTEM_DATA_ID_POSTFIX = "-system-rules";
/** 访问控制规则 */
public static final String AUTHORITY_DATA_ID_POSTFIX = "-authority-rules";

/** 网关流控规则 */
public static final String GATEWAY_FLOW_DATA_ID_POSTFIX = "-gw-flow-rules";
/** 网关API分组管理规则 */
public static final String GATEWAY_API_DATA_ID_POSTFIX = "-gw-api-group-rules";

添加效果如下图所示:

2.5、创建网关规则目录类

在src/main/java/com/alibaba/csp/sentinel/dashboard/rule/nacos创建如下目录结构

2.5.1、GatewayApiNacosProvider类

GatewayApiNacosProvider类:主要是拉取Nacos中手动添加的API分组管理配置信息,类代码如下

package com.alibaba.csp.sentinel.dashboard.rule.nacos.gateway.api;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * 拉取Nacos中存储的网关分组管理规则配置信息
 *
 * @author 星空流年
 */
@Component("gatewayApiNacosProvider")
public class GatewayApiNacosProvider implements DynamicRuleProvider<List<ApiDefinitionEntity>> {
    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<String, List<ApiDefinitionEntity>> converter;

    @Override
    public List<ApiDefinitionEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX, NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

2.5.2、GatewayApiNacosPublisher类

GatewayApiNacosPublisher类:主要是推送网关分组管理规则持久化到Nacos中

package com.alibaba.csp.sentinel.dashboard.rule.nacos.gateway.api;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 推送网关分组管理规则持久化到Nacos中
 *
 * @author 星空流年
 */
@Component("gatewayApiNacosPublisher")
public class GatewayApiNacosPublisher implements DynamicRulePublisher<List<ApiDefinitionEntity>> {
    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<ApiDefinitionEntity>, String> converter;

    @Override
    public void publish(String app, List<ApiDefinitionEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app + NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, converter.convert(rules), ConfigType.JSON.getType());
    }
}

2.5.3、GatewayFlowRulesNacosProvider类

package com.alibaba.csp.sentinel.dashboard.rule.nacos.gateway.flow;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * 拉取Nacos中存储的网关流控规则配置信息
 *
 * @author 星空流年
 */
@Component("gatewayFlowRulesNacosProvider")
public class GatewayFlowRulesNacosProvider implements DynamicRuleProvider<List<GatewayFlowRuleEntity>> {

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<String, List<GatewayFlowRuleEntity>> converter;

    @Override
    public List<GatewayFlowRuleEntity> getRules(String app) throws Exception {
        String rules = configService.getConfig(app + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

2.5.4、GatewayFlowRulesNacosPunlisher类

package com.alibaba.csp.sentinel.dashboard.rule.nacos.gateway.flow;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 推送网关流控规则持久化到Nacos中
 *
 * @author 星空流年
 */
@Component("gatewayFlowRulesNacosPublisher")
public class GatewayFlowRulesNacosPunlisher implements DynamicRulePublisher<List<GatewayFlowRuleEntity>> {

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<GatewayFlowRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<GatewayFlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }

        configService.publishConfig(app + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, converter.convert(rules), ConfigType.JSON.getType());
    }
}

2.6、修改GatewayApiController接口

GatewayApiController类所在路径:src/main/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiController.java

主要包含以下接口:

URL
方法名
请求方式
接口说明
/gateway/api/list.json
queryApis
GET
获取所有的API分组管理规则列表
/gateway/api/new.json
addApi
POST
新增API分组
/gateway/api/save.json
updateApi
POST
修改当前存在的API分组信息
/gateway/api/delete.json
deleteApi
POST
删除一条API分组信息

修改GatewayApiController接口就是对上面所列接口进行改造,将其从内存中获取规则的方式修改为从nacos中获取,同时将其持久化方式由持久化到内存修改为持久化到nacos。具体修改如下:

2.6.1、修改注入类

/*@Autowired
  private SentinelApiClient sentinelApiClient;*/

@Autowired
@Qualifier("gatewayApiNacosProvider")
private DynamicRuleProvider<List<ApiDefinitionEntity>> ruleProvider;

@Autowired
@Qualifier("gatewayApiNacosPublisher")
private DynamicRulePublisher<List<ApiDefinitionEntity>> rulePublisher;

说明:

SentinelApiClient类主要负责与 Sentinel 客户端通信,会发送HTTP调用客户端的API接口进行数据交互。其中主要是通过定义的一些方法将网关规则从内存中进行存取操作,具体方法可以查看相关代码。由于当前需要对网关规则进行从nacos存取操作,所以这里将其进行注释掉

2.6.2、修改queryApis方法

修改地方如图所示:

修改之后的代码如下:

代码如下:

import cn.hutool.core.collection.CollectionUtil;

@GetMapping("/list.json")
@AuthAction(AuthService.PrivilegeType.READ_RULE)
public Result<List<ApiDefinitionEntity>> queryApis(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<ApiDefinitionEntity> apis = ruleProvider.getRules(app);
        if (CollectionUtil.isNotEmpty(apis)) {
            for (ApiDefinitionEntity rule : apis) {
                rule.setApp(app.trim());
                rule.setIp(ip);
                rule.setPort(port);
            }
            repository.saveAll(apis);
        }
        return Result.ofSuccess(apis);
    } catch (Throwable throwable) {
        logger.error("queryApis error:", throwable);
        return Result.ofThrowable(-1, throwable);
    }
}

2.6.3、修改addApi方法

修改地方如图所示:

修改效果如下:

代码如下:

try {
      // 设置ID
      List<ApiDefinitionEntity> rules = ruleProvider.getRules(entity.getApp());
      if (CollectionUtil.isNotEmpty(rules)) {
          Optional<ApiDefinitionEntity> apiRule = rules.stream().max(Comparator.comparingLong(ApiDefinitionEntity::getId));
          entity.setId(apiRule.get().getId() + 1L);
      }

      entity = repository.save(entity);
} catch (Throwable throwable) {
    logger.error("add gateway api error:", throwable);
    return Result.ofThrowable(-1, throwable);
}
publishApis(app);

改造之后的publishApis()方法如下:

代码如下:

/**
 * 将API分组管理规则推送到Nacos
 * 
 * @param app
 */
private void publishApis(String app) {
   List<ApiDefinitionEntity> apis = repository.findAllByApp(app);
   try {
       rulePublisher.publish(app, apis);
   } catch (Exception e) {
       logger.warn("publish gateway apis fail, the exception is {}", e);
   }
}

2.6.4、修改updateApi方法

修改地方如下:

修改效果如下:

代码如下:

publishApis(app);

2.6.5、修改deleteApi方法

修改地方如下:

修改效果如下:

代码如下:

publishApis(oldEntity.getApp());

2.7、修改GatewayFlowRuleController接口

GatewayFlowRuleController类所在路径:src/main/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleController.java

主要包含以下接口:

URL
方法名
请求方式
接口说明
/gateway/flow/list.json
queryFlowRules
GET
获取所有的网关流控规则列表
/gateway/flow/new.json
addFlowRule
POST
新增网关流控规则
/gateway/flow/save.json
updateFlowRule
POST
修改当前存在的网关流控规则
/gateway/flow/delete.json
deleteFlowRule
POST
删除一条网关流控规则

修改GatewayFlowRuleController接口就是对上面所列接口进行改造,将其从内存中获取规则的方式修改为从nacos中获取,同时将其持久化方式由持久化到内存修改为持久化到nacos。具体修改如下:

2.7.1、修改注入类

/*@Autowired
  private SentinelApiClient sentinelApiClient;*/

@Autowired
@Qualifier("gatewayFlowRulesNacosProvider")
private DynamicRuleProvider<List<GatewayFlowRuleEntity>> ruleProvider;

@Autowired
@Qualifier("gatewayFlowRulesNacosPublisher")
private DynamicRulePublisher<List<GatewayFlowRuleEntity>> rulePublisher;

说明:

SentinelApiClient类主要负责与 Sentinel 客户端通信,会发送HTTP调用客户端的API接口进行数据交互。其中主要是通过定义的一些方法将网关规则从内存中进行存取操作,具体方法可以查看相关代码。由于当前需要对网关规则进行从nacos存取操作,所以这里将其进行注释掉

2.7.2、修改queryFlowRules方法

修改地方如图所示:

修改之后的代码如下:

import cn.hutool.core.collection.CollectionUtil;
import java.util.Objects;

try {
          List<GatewayFlowRuleEntity> rules = ruleProvider.getRules(app);
          if (CollectionUtil.isNotEmpty(rules)) {
              for (GatewayFlowRuleEntity rule : rules) {
                  rule.setApp(app.trim());
                  rule.setIp(ip);
                  rule.setPort(port);
                  // intervalSec:统计时间窗口,单位是秒,默认是1秒
                  if (Objects.isNull(rule.getInterval())) {
                      rule.setInterval(1L);
                  }
                  if (Objects.isNull(rule.getIntervalUnit())) {
                      rule.setIntervalUnit(INTERVAL_UNIT_SECOND);
                  }

                  // controlBehavior:流量整型的控制效果,同限流规则的 controlBehavior 字段
                  // 目前支持快速失败和匀速排队两种模式,默认是快速失败
                  if (Objects.isNull(rule.getControlBehavior())) {
                      rule.setControlBehavior(0);
                  }

                  // burst:应对突发请求时额外允许的请求数目
                  if (Objects.isNull(rule.getBurst())) {
                      rule.setBurst(0);
                  }
              }
              repository.saveAll(rules);
          }
          return Result.ofSuccess(rules);
} catch (Throwable throwable) {
        logger.error("query gateway flow rules error:", throwable);
        return Result.ofThrowable(-1, throwable);
}

2.7.3、修改addFlowRule方法

修改地方如下:

修改效果如下:

代码如下:

try {
     // 设置ID
     List<GatewayFlowRuleEntity> rules = ruleProvider.getRules(entity.getApp());
     if (CollectionUtil.isNotEmpty(rules)) {
         Optional<GatewayFlowRuleEntity> gatewayFlowRule = rules.stream().max(Comparator.comparingLong(GatewayFlowRuleEntity::getId));
               entity.setId(gatewayFlowRule.get().getId() + 1L);
           }

     entity = repository.save(entity);
} catch (Throwable throwable) {
     logger.error("add gateway flow rule error:", throwable);
     return Result.ofThrowable(-1, throwable);
}

publishRules(app);

改造之后的publishRules方法如下:代码如下:

/**
 * 将网关流控规则推送到Nacos
 *
 * @param app
 */
private void publishRules(String app) {
    List<GatewayFlowRuleEntity> rules = repository.findAllByApp(app);
    try {
        rulePublisher.publish(app, rules);
    } catch (Exception e) {
        logger.warn("publish gateway flow rules fail, the exception is {}", e);;
    }
}

2.7.4、修改updateFlowRule方法

修改地方如下:

修改效果如下:

代码如下:

publishRules(app);

2.7.5、修改deleteFlowRule方法

修改地方如下:

修改效果如下:

代码如下:

publishRules(oldEntity.getApp());

3、sentinel-dashboard中其他规则持久化改造

说明:关于sentinel其他规则,例如:流控、降级、访问控制、热点规则、系统保护规则等,这部分规则持久化到Nacos的改造在上面目录结构的基础上进行修改。其中这里提到的流控和网关流控规则不一致,至于二者区别,可参看官网文档,地址如下:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

3.1、创建其他规则目录类

在 src/main/java/com/alibaba/csp/sentinel/dashboard/rule/nacos 包名下创建如下目录结构

3.1.1、访问控制规则类代码

3.1.1.1、AuthorityRuleNacosProvider类

AuthorityRuleNacosProvider类:主要是拉取Nacos中手动添加的访问控制规则配置信息,类代码如下

package com.alibaba.csp.sentinel.dashboard.rule.nacos.authority;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 星空流年
 */
@Component("authorityRuleNacosProvider")
public class AuthorityRuleNacosProvider implements DynamicRuleProvider<List<AuthorityRuleEntity>> {

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<String, List<AuthorityRuleEntity>> converter;

    @Override
    public List<AuthorityRuleEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.AUTHORITY_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}
3.1.1.2、AuthorityRuleNacosPublisher类

AuthorityRuleNacosPublisher类:主要是推送访问控制规则持久化到Nacos中,具体代码如下:

package com.alibaba.csp.sentinel.dashboard.rule.nacos.authority;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author 星空流年
 */
@Component("authorityRuleNacosPublisher")
public class AuthorityRuleNacosPublisher implements DynamicRulePublisher<List<AuthorityRuleEntity>> {

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<AuthorityRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<AuthorityRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app + NacosConfigUtil.AUTHORITY_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, converter.convert(rules), ConfigType.JSON.getType());
    }
}

3.1.2、降级规则类代码

3.1.2.1、DegradeRuleNacosProvider类
package com.alibaba.csp.sentinel.dashboard.rule.nacos.degrade;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 星空流年
 */
@Component("degradeRuleNacosProvider")
public class DegradeRuleNacosProvider implements DynamicRuleProvider<List<DegradeRuleEntity>> {

    @Autowired
    private ConfigService configService;
    
    @Autowired
    private Converter<String, List<DegradeRuleEntity>> converter;

    @Override
    public List<DegradeRuleEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}
3.1.2.2、DegradeRuleNacosPublisher类
package com.alibaba.csp.sentinel.dashboard.rule.nacos.degrade;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author 星空流年
 */
@Component("degradeRuleNacosPublisher")
public class DegradeRuleNacosPublisher implements DynamicRulePublisher<List<DegradeRuleEntity>> {

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<DegradeRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<DegradeRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, converter.convert(rules), ConfigType.JSON.getType());
    }
}

3.1.3、普通流控类代码

3.1.3.1、FlowRuleNacosProvider类
package com.alibaba.csp.sentinel.dashboard.rule.nacos.flow;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 星空流年
 */
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<String, List<FlowRuleEntity>> converter;

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}
3.1.3.2、FlowRuleNacosPublisher类
package com.alibaba.csp.sentinel.dashboard.rule.nacos.flow;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author 星空流年
 */
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<FlowRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, converter.convert(rules), ConfigType.JSON.getType());
    }
}

3.1.4、热点规则类代码

3.1.4.1、ParamFlowRuleNacosProvider类
package com.alibaba.csp.sentinel.dashboard.rule.nacos.param;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 星空流年
 */
@Component("paramFlowRuleNacosProvider")
public class ParamFlowRuleNacosProvider implements DynamicRuleProvider<List<ParamFlowRuleEntity>> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<ParamFlowRuleEntity>> converter;

    @Override
    public List<ParamFlowRuleEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}
3.1.4.2、ParamFlowRuleNacosPublisher类
package com.alibaba.csp.sentinel.dashboard.rule.nacos.param;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author 星空流年
 */
@Component("paramFlowRuleNacosPublisher")
public class ParamFlowRuleNacosPublisher implements DynamicRulePublisher<List<ParamFlowRuleEntity>> {

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<ParamFlowRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<ParamFlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, converter.convert(rules), ConfigType.JSON.getType());
    }
}

3.1.5、系统保护类代码

3.1.5.1、SystemRuleNacosProvider类
package com.alibaba.csp.sentinel.dashboard.rule.nacos.system;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 星空流年
 */
@Component("systemRuleNacosProvider")
public class SystemRuleNacosProvider implements DynamicRuleProvider<List<SystemRuleEntity>> {

    @Autowired
    private ConfigService configService;
    
    @Autowired
    private Converter<String, List<SystemRuleEntity>> converter;

    @Override
    public List<SystemRuleEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.SYSTEM_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}
3.1.5.2、SystemRuleNacosPublisher类
package com.alibaba.csp.sentinel.dashboard.rule.nacos.system;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author 星空流年
 */
@Component("systemRuleNacosPublisher")
public class SystemRuleNacosPublisher implements DynamicRulePublisher<List<SystemRuleEntity>> {

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<SystemRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<SystemRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app + NacosConfigUtil.SYSTEM_DATA_ID_POSTFIX,
            NacosConfigUtil.GROUP_ID, converter.convert(rules), ConfigType.JSON.getType());
    }
}

3.2、修改AuthorityRuleController接口

AuthorityRuleController类所在路径:src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthorityRuleController.java

主要包含以下接口:

URL
方法名
请求方式
接口说明
/authority/rules
apiQueryAllRulesForMachine
GET
获取所有的访问控制规则列表
/authority/rule
apiAddAuthorityRule
POST
新增访问控制规则分组
/authority/rule/{id}
apiUpdateParamFlowRule
PUT
修改当前存在的访问控制规则信息
/authority/rule/{id}
apiDeleteRule
DELETE
删除一条访问控制规则信息

3.2.1、修改注入类

/*@Autowired
  private SentinelApiClient sentinelApiClient;*/

@Autowired
@Qualifier("authorityRuleNacosProvider")
private DynamicRuleProvider<List<AuthorityRuleEntity>> ruleProvider;

@Autowired
@Qualifier("authorityRuleNacosPublisher")
private DynamicRulePublisher<List<AuthorityRuleEntity>> rulePublisher;

3.2.2、修改apiQueryAllRulesForMachine方法

代码如下:

List<AuthorityRuleEntity> rules = ruleProvider.getRules(app);

3.2.3、修改apiAddAuthorityRule方法

添加代码如下:

publishRules(entity.getApp());

重写的publishRules()方法如下:

代码如下:

private void publishRules(String app) {
    List<AuthorityRuleEntity> rules = repository.findAllByApp(app);
    try {
        rulePublisher.publish(app,rules);
    } catch (Exception e) {
        logger.warn("publish authority rule fail, the exception is {}", e);
    }
}

3.2.4、修改apiUpdateParamFlowRule方法

说明:源码中此方法命名有误,关于错误原因,大家应该能猜出来,如果对此方法名不在意的,不需修改,若是在意,请自行修改,故在此进行说明

添加代码如下:

publishRules(entity.getApp());

3.2.5、修改apiDeleteRule方法

添加代码如下:

publishRules(oldEntity.getApp());

3.3、修改DegradeController接口

DegradeController类所在路径:src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DegradeController.java

主要包含以下接口:

URL方法名请求方式接口说明
/degrade/rules.json apiQueryMachineRules GET 获取所有的降级控制规则列表
/degrade/rule apiAddRule POST 新增降级规则分组
/degrade/rule/{id} apiUpdateRule PUT 修改当前存在的降级规则信息
/degrade/rule/{id} delete DELETE 删除一条降级规则信息

3.3.1、修改注入类

/*@Autowired
  private SentinelApiClient sentinelApiClient;*/
  
  @Autowired
  @Qualifier("degradeRuleNacosProvider")
  private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;
  
  @Autowired
  @Qualifier("degradeRuleNacosPublisher")
  private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;

3.3.2、修改apiQueryMachineRules方法

添加代码如下:

List<DegradeRuleEntity> rules = ruleProvider.getRules(app);

3.3.3、修改apiAddRule方法

添加代码如下:

publishRules(entity.getApp());

重写的publishRules()方法如下:

代码如下:

private void publishRules(String app) {
    List<DegradeRuleEntity> rules = repository.findAllByApp(app);
    try {
        rulePublisher.publish(app,rules);
    } catch (Exception e) {
        logger.warn("publish degrade rule fail, the exception is {}", e);
    }
}

3.3.4、修改apiUpdateRule方法

添加代码如下:

publishRules(entity.getApp());

3.3.5、修改delete方法

添加代码如下:

publishRules(oldEntity.getApp());

3.4、修改FlowControllerV2接口注入

FlowControllerV2类所在路径:src/main/java/com/alibaba/csp/sentinel/dashboard/controller/v2/FlowControllerV2.java

说明:FlowControllerV2类为官方提供的一个参考实例类,针对此类的修改只需要将注入依赖修改为自定义的持久化Nacos类即可。

修改如下:

代码如下:

/*@Autowired
  @Qualifier("flowRuleDefaultProvider")
  private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
  @Autowired
  @Qualifier("flowRuleDefaultPublisher")
  private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;*/

@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;

@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

3.5、修改ParamFlowRuleController接口

ParamFlowRuleController类所在路径:src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ParamFlowRuleController.java

主要包含以下接口:

URL
方法名
请求方式
接口说明
/paramFlow/rules
apiQueryAllRulesForMachine
GET
获取所有的热点规则列表
/paramFlow/rule
apiAddParamFlowRule
POST
新增热点规则分组
/paramFlow/rule/{id}
apiUpdateParamFlowRule
PUT
修改当前存在的热点规则信息
/paramFlow/rule/{id}
apiDeleteRule
DELETE
删除一条热点规则信息

3.5.1、修改注入类

/*@Autowired
  private SentinelApiClient sentinelApiClient;*/

  @Autowired
  @Qualifier("paramFlowRuleNacosProvider")
  private DynamicRuleProvider<List<ParamFlowRuleEntity>> ruleProvider;

  @Autowired
  @Qualifier("paramFlowRuleNacosPublisher")
  private DynamicRulePublisher<List<ParamFlowRuleEntity>> rulePublisher;

3.5.2、修改apiQueryAllRulesForMachine方法

添加代码如下:

List<ParamFlowRuleEntity> rules = ruleProvider.getRules(app);
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);

3.5.3、修改apiAddParamFlowRule方法

添加代码如下:

publishRules(entity.getApp());

重写publishRules()方法如下:

 

代码如下:

private void publishRules(String app) throws Exception {
    List<ParamFlowRuleEntity> rules=repository.findAllByApp(app);
    rulePublisher.publish(app, rules);
}

3.5.4、修改apiUpdateParamFlowRule方法

添加代码如下:

publishRules(entity.getApp());

3.5.5、修改apiDeleteRule方法

添加代码如下:

publishRules(oldEntity.getApp());

3.6、修改SystemController接口

SystemController类所在路径:src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java

主要包含以下接口:

URL
方法名
请求方式
接口说明
/system/rules.json
apiQueryMachineRules
GET
获取所有的系统保护规则列表
/system/new.json
apiAdd
POST
新增系统保护规则分组
/system/save.json
apiUpdateIfNotNull
PUT
修改当前存在的系统保护规则信息
/system/delete.json
delete
DELETE
删除一条系统保护规则信息

3.6.1、修改注入类

/*@Autowired
  private SentinelApiClient sentinelApiClient;*/
  
  @Autowired
  @Qualifier("systemRuleNacosProvider")
  private DynamicRuleProvider<List<SystemRuleEntity>> ruleProvider;
  
  @Autowired
  @Qualifier("systemRuleNacosPublisher")
  private DynamicRulePublisher<List<SystemRuleEntity>> rulePublisher;

3.6.2、修改apiQueryMachineRules方法

 

添加代码如下:

List<SystemRuleEntity> rules =ruleProvider.getRules(app);

3.6.3、修改apiAdd方法

添加代码如下:

publishRules(app);

重写publishRules()方法:

代码如下:

private void publishRules(String app) {
    List<SystemRuleEntity> rules = repository.findAllByApp(app);
    try {
        rulePublisher.publish(app,rules);
    } catch (Exception e) {
        logger.warn("publish system rule fail, the exception is {}", e);
    }
}

3.6.4、修改apiUpdateIfNotNull方法

添加代码如下:

publishRules(app);

3.6.5、修改delete方法

添加代码如下:

publishRules(oldEntity.getApp());

4、启动项目测试

4.1、后端项目启动

在IDE中运行 com.alibaba.csp.sentinel.dashboard.DashboardApplication类启动项目即可

4.2、前端项目启动

1、修改 src/main/webapp/resources/gulpfile.js 中的代码

2、修改 src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html 页面,将流控规则菜单中的dashboard.flowV1改为dashboard.flow,即可调用V2相关页面和接口

说明:前端项目为angular项目,启动方式可以参看 src/main/webapp/resources/README_zh.md 文档,

当执行 npm start 命令时,如果出现如下错误:

 针对此错误,可使用如下解决方案:

在package.json文件的同级目录下新建一个npm-shrinkwrap.json文件,如下所示

npm-shrinkwrap.json文件内容如下:

{
  "dependencies": {
    "graceful-fs": {
      "version": "4.2.2"
    }
  }
}

然后运行npm install命令,此文件只会更新npm-shrinkwrap.json文件,不会影响package.json文件。

4.3、运行界面

posted @ 2022-04-27 15:49  星空流年  阅读(2641)  评论(12编辑  收藏  举报