(企业案例)使用Nacos持久化规则,改造sentinel-dashboard

文章目录

          • 一、前言
            • 1. 版本选取
            • 2. 克隆代码
            • 3. 导入 IDEA
          • 二、全局修改
            • 2.1. 修改 POM
            • 2.2. 修改配置文件
          • 三、后端代码修改
            • 3.1. 包结构部分
            • 3.2. nacos 配置文件
          • 四、创建规则与 Nacos 交互类
            • 4.1. 创建授权规则与 Nacos 交互类
            • 4.2. 创建降级规则与 Nacos 交互类
            • 4.3. 创建流控规则与 Nacos 交互类
            • 4.4. 创建网关流控规则与 Nacos 交互类
            • 4.5. 创建热点规则与 Nacos 交互类
            • 4.6. 创建系统规则与 Nacos 交互类
          • 五、控制层修改
            • 5.1. AuthorityRuleController
            • 5.2. DegradeController
            • 5.3. FlowControllerV2
            • 5.4. GatewayApiController
            • 5.5. GatewayFlowRuleController
            • 5.6. ParamFlowRuleController
            • 5.7. SystemController
          • 六、前端代码调整
            • 6.1. sidebar.html
            • 6.2. flow_service_v1.js
            • 6.3. identity.js
            • 6.4. flow_v2.html
          • 七、修改的文件总览
            • 7.1. 后端文件列表
            • 7.2. 前端文件列表
          • 八、编译打包
            • 8.1. 编译前端
            • 8.2. 编译打包后端
          • 九、测试验证
            • 9.1. 版本选取
            • 9.2. 启动nacos
            • 9.3. 启动sentinel-dashboard
            • 9.4. 添加流控规则
            • 9.5. 重启sentinel-dashboard
            • 9.6. 删除流控规则
            • 9.7. nacos删除流控规则
            • 9.8. nacos修改流控规则
            • 9.9. 测试结论
          • 十、微服务集成
            • 10.1. pom依赖引入
            • 10.2. sentinel规则持久化配置
            • 10.3. nacos效果图

一、前言
1. 版本选取

要想改造 sentinel-dashboard,需要修改 dashboard 的源码。(本文基于sentinel 1.8.2)

2. 克隆代码

先通过 Github 拉取 Sentinel 源码,GitHub 地址:https://github.com/alibaba/Sentinel
在这里插入图片描述

3. 导入 IDEA

下载依赖,进入 sentinel-dashboard 模块,所有的修改都在该模块下。
在这里插入图片描述

二、全局修改
2.1. 修改 POM

想要将 dashboard 的规则持久化到 Nacos,需要添加一个依赖,源码中已经添加,不过只在测试环境下打包进去了。
在 pom.xml 文件中找到以下依赖,去掉 scope 或注释掉。

<!-- for Nacos rule publisher sample -->
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId><!-- <scope>test</scope> -->
</dependency>

在这里插入图片描述

2.2. 修改配置文件

找到配置文件 application.properties 文件,在末尾添加一下配置:

#Nacos Configurations
sentinel.nacos.serverAddr=localhost:8848
sentinel.nacos.namespace=
sentinel.nacos.group-id=SENTINEL-GROUP

注:sentinel.nacos.serverAddr 是 Nacos 服务器地址,如果是集群则用逗号隔开
在这里插入图片描述

三、后端代码修改
3.1. 包结构部分

新建包 com.alibaba.csp.sentinel.dashboard.rule.nacos,并在该包下根据以下结构新建包

├─auth #存放授权规则相关类
├─degrade #存放降级规则相关类
├─flow #存放限流规则相关类
├─gateway #存放网关限流规则相关类
│  ├─api
│  └─flow
├─param #存放热点规则相关类
└─system #存放系统规则相关类
3.2. nacos 配置文件

在这里插入图片描述

直接在com.alibaba.csp.sentinel.dashboard.rule.nacos包下创建以下类:
NacosPropertiesConfig,存放前面配置文件中关于 Nacos 的配置

package com.alibaba.csp.sentinel.dashboard.rule.nacos;import org.springframework.boot.context.properties.ConfigurationProperties;/*** @author gblfy* @date 2021-08-05*/
@ConfigurationProperties("sentinel.nacos")
public class NacosPropertiesConfig {private String serverAddr;private String dataId;private String groupId = "SENTINEL_GROUP"; // 默认分组private String namespace;public String getServerAddr() {return serverAddr;}public void setServerAddr(String serverAddr) {this.serverAddr = serverAddr;}public String getDataId() {return dataId;}public void setDataId(String dataId) {this.dataId = dataId;}public String getGroupId() {return groupId;}public void setGroupId(String groupId) {this.groupId = groupId;}public String getNamespace() {return namespace;}public void setNamespace(String namespace) {this.namespace = namespace;}
}

NacosConfigUtil,存放一些常量,各种规则文件名后缀。

package com.alibaba.csp.sentinel.dashboard.rule.nacos;/*** @author gblfy* @date 2021-08-05*/
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 PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";public static final String SYS_DATA_ID_POSTFIX = "-system-rules";public static final String AUTH_DATA_ID_POSTFIX = "-auth-rules";public static final String GATEWAY_FLOW_DATA_ID_POSTFIX = "-gateway-flow";public static final String GATEWAY_API_DATA_ID_POSTFIX = "-gateway-api";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() {}
}

③ 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.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.List;
import java.util.Properties;/*** @author gblfy* @date 2021-08-05*/
@EnableConfigurationProperties(NacosPropertiesConfig.class)
@Configuration
public class NacosConfig {//====================流控规则 Converter@Beanpublic Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {return s -> JSON.parseArray(s, FlowRuleEntity.class);}//====================降级规则 Converter@Beanpublic Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {return s -> JSON.parseArray(s, DegradeRuleEntity.class);}//====================热点规则 Converter@Beanpublic Converter<List<ParamFlowRuleEntity>, String> paramsRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<String, List<ParamFlowRuleEntity>> paramsRuleEntityDecoder() {return s -> JSON.parseArray(s, ParamFlowRuleEntity.class);}//====================系统规则 Converter@Beanpublic Converter<List<SystemRuleEntity>, String> systemRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<String, List<SystemRuleEntity>> systemRuleEntityDecoder() {return s -> JSON.parseArray(s, SystemRuleEntity.class);}//====================授权规则 Converter@Beanpublic Converter<List<AuthorityRuleEntity>, String> authRuleEntityEncoder() {return JSON::toJSONString;}@BeanConverter<String, List<AuthorityRuleEntity>> authRuleEntityDecoder() {return s -> JSON.parseArray(s, AuthorityRuleEntity.class);}//====================网关限流规则 Converter@Beanpublic Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {return JSON::toJSONString;}@BeanConverter<String, List<GatewayFlowRuleEntity>> gatewayFlowRuleEntityDecoder() {return s -> JSON.parseArray(s, GatewayFlowRuleEntity.class);}//====================网关API限流规则 Converter@Beanpublic Converter<List<ApiDefinitionEntity>, String> gatewayApiRuleEntityEncoder() {return JSON::toJSONString;}@BeanConverter<String, List<ApiDefinitionEntity>> gatewayApiRuleEntityDecoder() {return s -> JSON.parseArray(s, ApiDefinitionEntity.class);}@Beanpublic ConfigService nacosConfigService(NacosPropertiesConfig nacosPropertiesConfig) throws Exception {Properties properties = new Properties();properties.put(PropertyKeyConst.SERVER_ADDR, nacosPropertiesConfig.getServerAddr());properties.put(PropertyKeyConst.NAMESPACE, nacosPropertiesConfig.getNamespace());return ConfigFactory.createConfigService(properties);}
}
四、创建规则与 Nacos 交互类
4.1. 创建授权规则与 Nacos 交互类

在前面创建的auth包下创建以下两个类:
AuthorityRuleNacosProvider
AuthorityRuleNacosProvider,内容如下:

package com.alibaba.csp.sentinel.dashboard.rule.nacos.auth;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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;/*** @author gblfy* @date 2021-08-05*/
@Component("authRuleNacosProvider")
public class AuthorityRuleNacosProvider implements DynamicRuleProvider<List<AuthorityRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(AuthorityRuleNacosProvider.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<AuthorityRuleEntity>> converter;@Overridepublic List<AuthorityRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.AUTH_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, 3000);LOGGER.info("get auth rule from nacos, rules : {}", rules);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}

AuthorityRuleNacosPublisher,内容如下:

package com.alibaba.csp.sentinel.dashboard.rule.nacos.auth;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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.List;/*** @author gblfy* @date 2021-08-05*/
@Component("authRuleNacosPublisher")
public class AuthorityRuleNacosPublisher implements DynamicRulePublisher<List<AuthorityRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(AuthorityRuleNacosPublisher.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<AuthorityRuleEntity>, String> converter;@Overridepublic void publish(String app, List<AuthorityRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}String convertedRule = converter.convert(rules);LOGGER.info("sentinel dashboard publisher auth rules : {}", convertedRule);configService.publishConfig(app + NacosConfigUtil.AUTH_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, convertedRule);}
}
4.2. 创建降级规则与 Nacos 交互类

和前面一样,在前面创建的degrade包下创建以下两个类:
①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.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;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.csp.sentinel.datasource.Converter;/*** @author gblfy* @date 2021-08-05*/
@Component("degradeRuleNacosProvider")
public class DegradeRuleNacosProvider implements DynamicRuleProvider<List<DegradeRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(DegradeRuleNacosProvider.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<DegradeRuleEntity>> converter;@Overridepublic List<DegradeRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, 3000);LOGGER.info("get degrade rules from nacos, rules: {}", rules);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}

②DegradeRuleNacosPublisher,内容如下:

package com.alibaba.csp.sentinel.dashboard.rule.nacos.degrade;import com.alibaba.csp.sentinel.dashboard.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.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.csp.sentinel.datasource.Converter;/*** @author gblfy* @date 2021-08-05*/
@Component("degradeRuleNacosPublisher")
public class DegradeRuleNacosPublisher implements DynamicRulePublisher<List<DegradeRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(DegradeRuleNacosPublisher.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<DegradeRuleEntity>, String> converter;@Overridepublic void publish(String app, List<DegradeRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}String convertedRule = converter.convert(rules);LOGGER.info("sentinel dashboard publish degrade rules: {}", convertedRule);configService.publishConfig(app + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, convertedRule);}
}
4.3. 创建流控规则与 Nacos 交互类

在前面创建的flow包下创建以下两个类:

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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;/*** @author gblfy* @date 2021-08-05*/
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(FlowRuleNacosProvider.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<FlowRuleEntity>> converter;@Overridepublic List<FlowRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, 3000);LOGGER.info("get flow rules from nacos, rules: {}", rules);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}

FlowRuleNacosPublisher,内容如下:

package com.alibaba.csp.sentinel.dashboard.rule.nacos.flow;import com.alibaba.csp.sentinel.dashboard.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.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.csp.sentinel.datasource.Converter;import java.util.List;/*** @author gblfy* @date 2021-08-05*/
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(FlowRuleNacosPublisher.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<FlowRuleEntity>, String> converter;@Overridepublic void publish(String app, List<FlowRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}String convertedRule = converter.convert(rules);LOGGER.info("sentinel dashboard publish flow rules: {}", convertedRule);configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, convertedRule);}
}
4.4. 创建网关流控规则与 Nacos 交互类
  • 在前面创建的gateway.api包下创建以下两个类:

GatewayApiRuleNacosProvider,内容如下:

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.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;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.csp.sentinel.datasource.Converter;/*** @author gblfy* @date 2021-08-05*/
@Component("gatewayApiRuleNacosProvider")
public class GatewayApiRuleNacosProvider implements DynamicRuleProvider<List<ApiDefinitionEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(GatewayApiRuleNacosProvider.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<ApiDefinitionEntity>> converter;@Overridepublic List<ApiDefinitionEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, 3000);LOGGER.info("get gateway api rules from nacos, rules: {}", rules);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}

GatewayApiRuleNacosPublisher,内容如下:

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.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.csp.sentinel.datasource.Converter;/*** @author gblfy* @date 2021-08-05*/
@Component("gatewayApiRuleNacosPublisher")
public class GatewayApiRuleNacosPublisher implements DynamicRulePublisher<List<ApiDefinitionEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(GatewayApiRuleNacosPublisher.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<ApiDefinitionEntity>, String> converter;@Overridepublic void publish(String app, List<ApiDefinitionEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}String convertedRule = converter.convert(rules);LOGGER.info("sentinel dashboard publish gateway api rules: {}", convertedRule);configService.publishConfig(app + NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, convertedRule);}
}
  • 在gateway.flow包下创建以下两个类:
    ①GatewayFlowRuleNacosProvider,内容如下:
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.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;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.csp.sentinel.datasource.Converter;/*** @author gblfy* @date 2021-08-05*/
@Component("gatewayFlowRuleNacosProvider")
public class GatewayFlowRuleNacosProvider implements DynamicRuleProvider<List<GatewayFlowRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(GatewayFlowRuleNacosProvider.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<GatewayFlowRuleEntity>> converter;@Overridepublic List<GatewayFlowRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, 3000);LOGGER.info("get gateway flow rules from nacos, rules: {}", rules);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}

②GatewayFlowRuleNacosPublisher,内容如下:

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.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.csp.sentinel.datasource.Converter;/*** @author gblfy* @date 2021-08-05*/
@Component("gatewayFlowRuleNacosPublisher")
public class GatewayFlowRuleNacosPublisher implements DynamicRulePublisher<List<GatewayFlowRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(GatewayFlowRuleNacosPublisher.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<GatewayFlowRuleEntity>, String> converter;@Overridepublic void publish(String app, List<GatewayFlowRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}String convertedRule = converter.convert(rules);LOGGER.info("sentinel dashboard publish gateway flow rules: {}", convertedRule);configService.publishConfig(app + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, convertedRule);}
}
4.5. 创建热点规则与 Nacos 交互类

在前面创建的param包下新建以下两个类:

ParamRuleNacosProvider,内容如下:

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.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.csp.sentinel.datasource.Converter;import java.util.ArrayList;
import java.util.List;/*** @author gblfy* @date 2021-08-05*/
@Component("paramRuleNacosProvider")
public class ParamRuleNacosProvider implements DynamicRuleProvider<List<ParamFlowRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(ParamRuleNacosProvider.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<ParamFlowRuleEntity>> converter;@Overridepublic List<ParamFlowRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, 3000);LOGGER.info("get param rules from nacos, rules: {}", rules);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}

ParamRuleNacosPublisher,内容如下:

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.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.csp.sentinel.datasource.Converter;import java.util.List;/*** @author gblfy* @date 2021-08-05*/
@Component("paramRuleNacosPublisher")
public class ParamRuleNacosPublisher implements DynamicRulePublisher<List<ParamFlowRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(ParamRuleNacosPublisher.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<ParamFlowRuleEntity>, String> converter;@Overridepublic void publish(String app, List<ParamFlowRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}String convertedRule = converter.convert(rules);LOGGER.info("sentinel dashboard publish param rules: {}", convertedRule);configService.publishConfig(app + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, convertedRule);}}
4.6. 创建系统规则与 Nacos 交互类

在前面创建的system包下新建以下两个类:

①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.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;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.csp.sentinel.datasource.Converter;/*** @author gblfy* @date 2021-08-05*/
@Component("systemRuleNacosProvider")
public class SystemRuleNacosProvider implements DynamicRuleProvider<List<SystemRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(SystemRuleNacosProvider.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<SystemRuleEntity>> converter;@Overridepublic List<SystemRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.SYS_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, 3000);LOGGER.info("get system rules from nacos, rules: {}", rules);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}

②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.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.csp.sentinel.datasource.Converter;import java.util.List;/*** @author gblfy* @date 2021-08-05*/
@Component("systemRuleNacosPublisher")
public class SystemRuleNacosPublisher implements DynamicRulePublisher<List<SystemRuleEntity>> {private static final Logger LOGGER = LoggerFactory.getLogger(SystemRuleNacosPublisher.class);@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<SystemRuleEntity>, String> converter;@Overridepublic void publish(String app, List<SystemRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}String convertedRule = converter.convert(rules);LOGGER.info("sentinel dashboard publisher system rule : {}", convertedRule);configService.publishConfig(app + NacosConfigUtil.SYS_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, convertedRule);}
}
五、控制层修改

修改 Controller

5.1. AuthorityRuleController

修改 com.alibaba.csp.sentinel.dashboard.controller包下的AuthorityRuleController,内容如下:

package com.alibaba.csp.sentinel.dashboard.controller;import java.util.Date;
import java.util.List;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** @author Eric Zhao* @since 0.2.1*/
@RestController
@RequestMapping(value = "/authority")
public class AuthorityRuleController {private final Logger logger = LoggerFactory.getLogger(AuthorityRuleController.class);@Autowiredprivate SentinelApiClient sentinelApiClient;@Autowiredprivate RuleRepository<AuthorityRuleEntity, Long> repository;@Autowired@Qualifier("authRuleNacosProvider")private DynamicRuleProvider<List<AuthorityRuleEntity>> ruleProvider;@Autowired@Qualifier("authRuleNacosPublisher")private DynamicRulePublisher<List<AuthorityRuleEntity>> rulePublisher;@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);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);} catch (Throwable throwable) {logger.error("Failed to add authority rule", throwable);return Result.ofThrowable(-1, throwable);}if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {logger.info("Publish authority rules failed after rule add");}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");}} catch (Throwable throwable) {logger.error("Failed to save authority rule", throwable);return Result.ofThrowable(-1, throwable);}if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {logger.info("Publish authority rules failed after rule update");}return Result.ofSuccess(entity);}@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");}AuthorityRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofSuccess(null);}try {repository.delete(id);} catch (Exception e) {return Result.ofFail(-1, e.getMessage());}if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {logger.error("Publish authority rules failed after rule delete");}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);try {rulePublisher.publish(app, rules);return true;} catch (Exception e) {return false;}}
}

主要是将SentinelApiClient相关调用改为我们前面创建的一系列NacosProvider和NacosPublisher类中的调用,与 Nacos 交互。

5.2. DegradeController

修改 com.alibaba.csp.sentinel.dashboard.controller包下的DegradeController,内容如下:

package com.alibaba.csp.sentinel.dashboard.controller;import java.util.Date;
import java.util.List;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
import com.alibaba.csp.sentinel.util.StringUtil;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.domain.Result;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** Controller regarding APIs of degrade rules. Refactored since 1.8.0.** @author Carpenter Lee* @author Eric Zhao*/
@RestController
@RequestMapping("/degrade")
public class DegradeController {private final Logger logger = LoggerFactory.getLogger(DegradeController.class);@Autowiredprivate RuleRepository<DegradeRuleEntity, Long> repository;@Autowiredprivate SentinelApiClient sentinelApiClient;@Autowired@Qualifier("degradeRuleNacosProvider")private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;@Autowired@Qualifier("degradeRuleNacosPublisher")private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;@GetMapping("/rules.json")@AuthAction(PrivilegeType.READ_RULE)public Result<List<DegradeRuleEntity>> apiQueryMachineRules(String app, String ip, Integer port) {if (StringUtil.isEmpty(app)) {return Result.ofFail(-1, "app can't be null or empty");}if (StringUtil.isEmpty(ip)) {return Result.ofFail(-1, "ip can't be null or empty");}if (port == null) {return Result.ofFail(-1, "port can't be null");}try {
//            List<DegradeRuleEntity> rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port);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);}}@PostMapping("/rule")@AuthAction(PrivilegeType.WRITE_RULE)public Result<DegradeRuleEntity> apiAddRule(@RequestBody DegradeRuleEntity entity) {Result<DegradeRuleEntity> checkResult = checkEntityInternal(entity);if (checkResult != null) {return checkResult;}Date date = new Date();entity.setGmtCreate(date);entity.setGmtModified(date);try {entity = repository.save(entity);} catch (Throwable t) {logger.error("Failed to add new degrade rule, app={}, ip={}", entity.getApp(), entity.getIp(), t);return Result.ofThrowable(-1, t);}if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {logger.warn("Publish degrade rules failed, app={}", entity.getApp());}return Result.ofSuccess(entity);}@PutMapping("/rule/{id}")@AuthAction(PrivilegeType.WRITE_RULE)public Result<DegradeRuleEntity> apiUpdateRule(@PathVariable("id") Long id,@RequestBody DegradeRuleEntity entity) {if (id == null || id <= 0) {return Result.ofFail(-1, "id can't be null or negative");}DegradeRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofFail(-1, "Degrade rule does not exist, id=" + id);}entity.setApp(oldEntity.getApp());entity.setIp(oldEntity.getIp());entity.setPort(oldEntity.getPort());entity.setId(oldEntity.getId());Result<DegradeRuleEntity> checkResult = checkEntityInternal(entity);if (checkResult != null) {return checkResult;}entity.setGmtCreate(oldEntity.getGmtCreate());entity.setGmtModified(new Date());try {entity = repository.save(entity);} catch (Throwable t) {logger.error("Failed to save degrade rule, id={}, rule={}", id, entity, t);return Result.ofThrowable(-1, t);}if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {logger.warn("Publish degrade rules failed, app={}", entity.getApp());}return Result.ofSuccess(entity);}@DeleteMapping("/rule/{id}")@AuthAction(PrivilegeType.DELETE_RULE)public Result<Long> delete(@PathVariable("id") Long id) {if (id == null) {return Result.ofFail(-1, "id can't be null");}DegradeRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofSuccess(null);}try {repository.delete(id);} catch (Throwable throwable) {logger.error("Failed to delete degrade rule, id={}", id, throwable);return Result.ofThrowable(-1, throwable);}if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {logger.warn("Publish degrade rules failed, app={}", oldEntity.getApp());}return Result.ofSuccess(id);}private boolean publishRules(String app, String ip, Integer port) {List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
//        return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);try{rulePublisher.publish(app, rules);return true;}catch (Exception e){return false;}}private <R> Result<R> checkEntityInternal(DegradeRuleEntity entity) {if (StringUtil.isBlank(entity.getApp())) {return Result.ofFail(-1, "app can't be blank");}if (StringUtil.isBlank(entity.getIp())) {return Result.ofFail(-1, "ip can't be null or empty");}if (entity.getPort() == null || entity.getPort() <= 0) {return Result.ofFail(-1, "invalid port: " + entity.getPort());}if (StringUtil.isBlank(entity.getLimitApp())) {return Result.ofFail(-1, "limitApp can't be null or empty");}if (StringUtil.isBlank(entity.getResource())) {return Result.ofFail(-1, "resource can't be null or empty");}Double threshold = entity.getCount();if (threshold == null || threshold < 0) {return Result.ofFail(-1, "invalid threshold: " + threshold);}Integer recoveryTimeoutSec = entity.getTimeWindow();if (recoveryTimeoutSec == null || recoveryTimeoutSec <= 0) {return Result.ofFail(-1, "recoveryTimeout should be positive");}Integer strategy = entity.getGrade();if (strategy == null) {return Result.ofFail(-1, "circuit breaker strategy cannot be null");}if (strategy < CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType()|| strategy > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {return Result.ofFail(-1, "Invalid circuit breaker strategy: " + strategy);}if (entity.getMinRequestAmount()  == null || entity.getMinRequestAmount() <= 0) {return Result.ofFail(-1, "Invalid minRequestAmount");}if (entity.getStatIntervalMs() == null || entity.getStatIntervalMs() <= 0) {return Result.ofFail(-1, "Invalid statInterval");}if (strategy == RuleConstant.DEGRADE_GRADE_RT) {Double slowRatio = entity.getSlowRatioThreshold();if (slowRatio == null) {return Result.ofFail(-1, "SlowRatioThreshold is required for slow request ratio strategy");} else if (slowRatio < 0 || slowRatio > 1) {return Result.ofFail(-1, "SlowRatioThreshold should be in range: [0.0, 1.0]");}} else if (strategy == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) {if (threshold > 1) {return Result.ofFail(-1, "Ratio threshold should be in range: [0.0, 1.0]");}}return null;}
}
5.3. FlowControllerV2

修改 com.alibaba.csp.sentinel.dashboard.controller.v2包下的FlowControllerV2,内容如下:

package com.alibaba.csp.sentinel.dashboard.controller.v2;import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.util.StringUtil;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.domain.Result;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** Flow rule controller (v2).** @author Eric Zhao* @since 1.4.0*/
@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);@Autowiredprivate InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;//    @Autowired
//    @Qualifier("flowRuleDefaultProvider")
//    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
//    @Autowired
//    @Qualifier("flowRuleDefaultPublisher")
//    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;//===============================注入Nacos Provider和 Publisher@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");}try {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.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(value = AuthService.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("/rule/{id}")@AuthAction(AuthService.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("/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");}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 void publishRules(/*@NonNull*/ String app) throws Exception {List<FlowRuleEntity> rules = repository.findAllByApp(app);rulePublisher.publish(app, rules);//发布后暂定一会,防止立即查询 仍然是旧数据TimeUnit.MILLISECONDS.sleep(500);}
}
5.4. GatewayApiController

修改 com.alibaba.csp.sentinel.dashboard.controller.gateway包下的GatewayApiController,内容如下:

package com.alibaba.csp.sentinel.dashboard.controller.gateway;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiPredicateItemEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.AddApiReqVo;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.ApiPredicateItemVo;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.UpdateApiReqVo;
import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemApiDefinitionStore;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletRequest;
import java.util.*;import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*;/*** Gateway api Controller for manage gateway api definitions.** @author cdfive* @since 1.7.0*/
@RestController
@RequestMapping(value = "/gateway/api")
public class GatewayApiController {private final Logger logger = LoggerFactory.getLogger(GatewayApiController.class);@Autowiredprivate InMemApiDefinitionStore repository;//    @Autowired
//    private SentinelApiClient sentinelApiClient;@Autowired@Qualifier("gatewayApiRuleNacosProvider")private DynamicRuleProvider<List<ApiDefinitionEntity>> provider;@Autowired@Qualifier("gatewayApiRuleNacosPublisher")private DynamicRulePublisher<List<ApiDefinitionEntity>> publisher;@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 = sentinelApiClient.fetchApis(app, ip, port).get();List<ApiDefinitionEntity> apis = provider.getRules(app);repository.saveAll(apis);return Result.ofSuccess(apis);} catch (Throwable throwable) {logger.error("queryApis error:", throwable);return Result.ofThrowable(-1, throwable);}}@PostMapping("/new.json")@AuthAction(AuthService.PrivilegeType.WRITE_RULE)public Result<ApiDefinitionEntity> addApi(HttpServletRequest request, @RequestBody AddApiReqVo reqVo) {String app = reqVo.getApp();if (StringUtil.isBlank(app)) {return Result.ofFail(-1, "app can't be null or empty");}ApiDefinitionEntity entity = new ApiDefinitionEntity();entity.setApp(app.trim());String ip = reqVo.getIp();if (StringUtil.isBlank(ip)) {return Result.ofFail(-1, "ip can't be null or empty");}entity.setIp(ip.trim());Integer port = reqVo.getPort();if (port == null) {return Result.ofFail(-1, "port can't be null");}entity.setPort(port);// API名称String apiName = reqVo.getApiName();if (StringUtil.isBlank(apiName)) {return Result.ofFail(-1, "apiName can't be null or empty");}entity.setApiName(apiName.trim());// 匹配规则列表List<ApiPredicateItemVo> predicateItems = reqVo.getPredicateItems();if (CollectionUtils.isEmpty(predicateItems)) {return Result.ofFail(-1, "predicateItems can't empty");}List<ApiPredicateItemEntity> predicateItemEntities = new ArrayList<>();for (ApiPredicateItemVo predicateItem : predicateItems) {ApiPredicateItemEntity predicateItemEntity = new ApiPredicateItemEntity();// 匹配模式Integer matchStrategy = predicateItem.getMatchStrategy();if (!Arrays.asList(URL_MATCH_STRATEGY_EXACT, URL_MATCH_STRATEGY_PREFIX, URL_MATCH_STRATEGY_REGEX).contains(matchStrategy)) {return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy);}predicateItemEntity.setMatchStrategy(matchStrategy);// 匹配串String pattern = predicateItem.getPattern();if (StringUtil.isBlank(pattern)) {return Result.ofFail(-1, "pattern can't be null or empty");}predicateItemEntity.setPattern(pattern);predicateItemEntities.add(predicateItemEntity);}entity.setPredicateItems(new LinkedHashSet<>(predicateItemEntities));// 检查API名称不能重复List<ApiDefinitionEntity> allApis = repository.findAllByMachine(MachineInfo.of(app.trim(), ip.trim(), port));if (allApis.stream().map(o -> o.getApiName()).anyMatch(o -> o.equals(apiName.trim()))) {return Result.ofFail(-1, "apiName exists: " + apiName);}Date date = new Date();entity.setGmtCreate(date);entity.setGmtModified(date);try {entity = repository.save(entity);} catch (Throwable throwable) {logger.error("add gateway api error:", throwable);return Result.ofThrowable(-1, throwable);}if (!publishApis(app, ip, port)) {logger.warn("publish gateway apis fail after add");}return Result.ofSuccess(entity);}@PostMapping("/save.json")@AuthAction(AuthService.PrivilegeType.WRITE_RULE)public Result<ApiDefinitionEntity> updateApi(@RequestBody UpdateApiReqVo reqVo) {String app = reqVo.getApp();if (StringUtil.isBlank(app)) {return Result.ofFail(-1, "app can't be null or empty");}Long id = reqVo.getId();if (id == null) {return Result.ofFail(-1, "id can't be null");}ApiDefinitionEntity entity = repository.findById(id);if (entity == null) {return Result.ofFail(-1, "api does not exist, id=" + id);}// 匹配规则列表List<ApiPredicateItemVo> predicateItems = reqVo.getPredicateItems();if (CollectionUtils.isEmpty(predicateItems)) {return Result.ofFail(-1, "predicateItems can't empty");}List<ApiPredicateItemEntity> predicateItemEntities = new ArrayList<>();for (ApiPredicateItemVo predicateItem : predicateItems) {ApiPredicateItemEntity predicateItemEntity = new ApiPredicateItemEntity();// 匹配模式int matchStrategy = predicateItem.getMatchStrategy();if (!Arrays.asList(URL_MATCH_STRATEGY_EXACT, URL_MATCH_STRATEGY_PREFIX, URL_MATCH_STRATEGY_REGEX).contains(matchStrategy)) {return Result.ofFail(-1, "Invalid matchStrategy: " + matchStrategy);}predicateItemEntity.setMatchStrategy(matchStrategy);// 匹配串String pattern = predicateItem.getPattern();if (StringUtil.isBlank(pattern)) {return Result.ofFail(-1, "pattern can't be null or empty");}predicateItemEntity.setPattern(pattern);predicateItemEntities.add(predicateItemEntity);}entity.setPredicateItems(new LinkedHashSet<>(predicateItemEntities));Date date = new Date();entity.setGmtModified(date);try {entity = repository.save(entity);} catch (Throwable throwable) {logger.error("update gateway api error:", throwable);return Result.ofThrowable(-1, throwable);}if (!publishApis(app, entity.getIp(), entity.getPort())) {logger.warn("publish gateway apis fail after update");}return Result.ofSuccess(entity);}@PostMapping("/delete.json")@AuthAction(AuthService.PrivilegeType.DELETE_RULE)public Result<Long> deleteApi(Long id) {if (id == null) {return Result.ofFail(-1, "id can't be null");}ApiDefinitionEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofSuccess(null);}try {repository.delete(id);} catch (Throwable throwable) {logger.error("delete gateway api error:", throwable);return Result.ofThrowable(-1, throwable);}if (!publishApis(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {logger.warn("publish gateway apis fail after delete");}return Result.ofSuccess(id);}private boolean publishApis(String app, String ip, Integer port) {List<ApiDefinitionEntity> apis = repository.findAllByMachine(MachineInfo.of(app, ip, port));try {publisher.publish(app, apis);return true;} catch (Exception e) {e.printStackTrace();return false;}}
}
5.5. GatewayFlowRuleController

修改 com.alibaba.csp.sentinel.dashboard.controller.gateway包下的GatewayFlowRuleController,内容如下:

package com.alibaba.csp.sentinel.dashboard.controller.gateway;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayParamFlowItemEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.AddFlowRuleReqVo;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.GatewayParamFlowItemVo;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.UpdateFlowRuleReqVo;
import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemGatewayFlowRuleStore;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;import java.util.Arrays;
import java.util.Date;
import java.util.List;import static com.alibaba.csp.sentinel.slots.block.RuleConstant.*;
import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*;
import static com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity.*;/*** Gateway flow rule Controller for manage gateway flow rules.** @author cdfive* @since 1.7.0*/
@RestController
@RequestMapping(value = "/gateway/flow")
public class GatewayFlowRuleController {private final Logger logger = LoggerFactory.getLogger(GatewayFlowRuleController.class);@Autowiredprivate InMemGatewayFlowRuleStore repository;
//
//    @Autowired
//    private SentinelApiClient sentinelApiClient;@Autowired@Qualifier("gatewayFlowRuleNacosProvider")private DynamicRuleProvider<List<GatewayFlowRuleEntity>> provider;@Autowired@Qualifier("gatewayFlowRuleNacosPublisher")private DynamicRulePublisher<List<GatewayFlowRuleEntity>> publisher;@GetMapping("/list.json")@AuthAction(AuthService.PrivilegeType.READ_RULE)public Result<List<GatewayFlowRuleEntity>> queryFlowRules(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<GatewayFlowRuleEntity> rules = sentinelApiClient.fetchGatewayFlowRules(app, ip, port).get();List<GatewayFlowRuleEntity> rules = provider.getRules(app);repository.saveAll(rules);return Result.ofSuccess(rules);} catch (Throwable throwable) {logger.error("query gateway flow rules error:", throwable);return Result.ofThrowable(-1, throwable);}}@PostMapping("/new.json")@AuthAction(AuthService.PrivilegeType.WRITE_RULE)public Result<GatewayFlowRuleEntity> addFlowRule(@RequestBody AddFlowRuleReqVo reqVo) {String app = reqVo.getApp();if (StringUtil.isBlank(app)) {return Result.ofFail(-1, "app can't be null or empty");}GatewayFlowRuleEntity entity = new GatewayFlowRuleEntity();entity.setApp(app.trim());String ip = reqVo.getIp();if (StringUtil.isBlank(ip)) {return Result.ofFail(-1, "ip can't be null or empty");}entity.setIp(ip.trim());Integer port = reqVo.getPort();if (port == null) {return Result.ofFail(-1, "port can't be null");}entity.setPort(port);// API类型, Route ID或API分组Integer resourceMode = reqVo.getResourceMode();if (resourceMode == null) {return Result.ofFail(-1, "resourceMode can't be null");}if (!Arrays.asList(RESOURCE_MODE_ROUTE_ID, RESOURCE_MODE_CUSTOM_API_NAME).contains(resourceMode)) {return Result.ofFail(-1, "invalid resourceMode: " + resourceMode);}entity.setResourceMode(resourceMode);// API名称String resource = reqVo.getResource();if (StringUtil.isBlank(resource)) {return Result.ofFail(-1, "resource can't be null or empty");}entity.setResource(resource.trim());// 针对请求属性GatewayParamFlowItemVo paramItem = reqVo.getParamItem();if (paramItem != null) {GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity();entity.setParamItem(itemEntity);// 参数属性 0-ClientIP 1-Remote Host 2-Header 3-URL参数 4-CookieInteger parseStrategy = paramItem.getParseStrategy();if (!Arrays.asList(PARAM_PARSE_STRATEGY_CLIENT_IP, PARAM_PARSE_STRATEGY_HOST, PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {return Result.ofFail(-1, "invalid parseStrategy: " + parseStrategy);}itemEntity.setParseStrategy(paramItem.getParseStrategy());// 当参数属性为2-Header 3-URL参数 4-Cookie时,参数名称必填if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {// 参数名称String fieldName = paramItem.getFieldName();if (StringUtil.isBlank(fieldName)) {return Result.ofFail(-1, "fieldName can't be null or empty");}itemEntity.setFieldName(paramItem.getFieldName());}String pattern = paramItem.getPattern();// 如果匹配串不为空,验证匹配模式if (StringUtil.isNotEmpty(pattern)) {itemEntity.setPattern(pattern);Integer matchStrategy = paramItem.getMatchStrategy();if (!Arrays.asList(PARAM_MATCH_STRATEGY_EXACT, PARAM_MATCH_STRATEGY_CONTAINS, PARAM_MATCH_STRATEGY_REGEX).contains(matchStrategy)) {return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy);}itemEntity.setMatchStrategy(matchStrategy);}}// 阈值类型 0-线程数 1-QPSInteger grade = reqVo.getGrade();if (grade == null) {return Result.ofFail(-1, "grade can't be null");}if (!Arrays.asList(FLOW_GRADE_THREAD, FLOW_GRADE_QPS).contains(grade)) {return Result.ofFail(-1, "invalid grade: " + grade);}entity.setGrade(grade);// QPS阈值Double count = reqVo.getCount();if (count == null) {return Result.ofFail(-1, "count can't be null");}if (count < 0) {return Result.ofFail(-1, "count should be at lease zero");}entity.setCount(count);// 间隔Long interval = reqVo.getInterval();if (interval == null) {return Result.ofFail(-1, "interval can't be null");}if (interval <= 0) {return Result.ofFail(-1, "interval should be greater than zero");}entity.setInterval(interval);// 间隔单位Integer intervalUnit = reqVo.getIntervalUnit();if (intervalUnit == null) {return Result.ofFail(-1, "intervalUnit can't be null");}if (!Arrays.asList(INTERVAL_UNIT_SECOND, INTERVAL_UNIT_MINUTE, INTERVAL_UNIT_HOUR, INTERVAL_UNIT_DAY).contains(intervalUnit)) {return Result.ofFail(-1, "Invalid intervalUnit: " + intervalUnit);}entity.setIntervalUnit(intervalUnit);// 流控方式 0-快速失败 2-匀速排队Integer controlBehavior = reqVo.getControlBehavior();if (controlBehavior == null) {return Result.ofFail(-1, "controlBehavior can't be null");}if (!Arrays.asList(CONTROL_BEHAVIOR_DEFAULT, CONTROL_BEHAVIOR_RATE_LIMITER).contains(controlBehavior)) {return Result.ofFail(-1, "invalid controlBehavior: " + controlBehavior);}entity.setControlBehavior(controlBehavior);if (CONTROL_BEHAVIOR_DEFAULT == controlBehavior) {// 0-快速失败, 则Burst size必填Integer burst = reqVo.getBurst();if (burst == null) {return Result.ofFail(-1, "burst can't be null");}if (burst < 0) {return Result.ofFail(-1, "invalid burst: " + burst);}entity.setBurst(burst);} else if (CONTROL_BEHAVIOR_RATE_LIMITER == controlBehavior) {// 1-匀速排队, 则超时时间必填Integer maxQueueingTimeoutMs = reqVo.getMaxQueueingTimeoutMs();if (maxQueueingTimeoutMs == null) {return Result.ofFail(-1, "maxQueueingTimeoutMs can't be null");}if (maxQueueingTimeoutMs < 0) {return Result.ofFail(-1, "invalid maxQueueingTimeoutMs: " + maxQueueingTimeoutMs);}entity.setMaxQueueingTimeoutMs(maxQueueingTimeoutMs);}Date date = new Date();entity.setGmtCreate(date);entity.setGmtModified(date);try {entity = repository.save(entity);} catch (Throwable throwable) {logger.error("add gateway flow rule error:", throwable);return Result.ofThrowable(-1, throwable);}if (!publishRules(app, ip, port)) {logger.warn("publish gateway flow rules fail after add");}return Result.ofSuccess(entity);}@PostMapping("/save.json")@AuthAction(AuthService.PrivilegeType.WRITE_RULE)public Result<GatewayFlowRuleEntity> updateFlowRule(@RequestBody UpdateFlowRuleReqVo reqVo) {String app = reqVo.getApp();if (StringUtil.isBlank(app)) {return Result.ofFail(-1, "app can't be null or empty");}Long id = reqVo.getId();if (id == null) {return Result.ofFail(-1, "id can't be null");}GatewayFlowRuleEntity entity = repository.findById(id);if (entity == null) {return Result.ofFail(-1, "gateway flow rule does not exist, id=" + id);}// 针对请求属性GatewayParamFlowItemVo paramItem = reqVo.getParamItem();if (paramItem != null) {GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity();entity.setParamItem(itemEntity);// 参数属性 0-ClientIP 1-Remote Host 2-Header 3-URL参数 4-CookieInteger parseStrategy = paramItem.getParseStrategy();if (!Arrays.asList(PARAM_PARSE_STRATEGY_CLIENT_IP, PARAM_PARSE_STRATEGY_HOST, PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {return Result.ofFail(-1, "invalid parseStrategy: " + parseStrategy);}itemEntity.setParseStrategy(paramItem.getParseStrategy());// 当参数属性为2-Header 3-URL参数 4-Cookie时,参数名称必填if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {// 参数名称String fieldName = paramItem.getFieldName();if (StringUtil.isBlank(fieldName)) {return Result.ofFail(-1, "fieldName can't be null or empty");}itemEntity.setFieldName(paramItem.getFieldName());}String pattern = paramItem.getPattern();// 如果匹配串不为空,验证匹配模式if (StringUtil.isNotEmpty(pattern)) {itemEntity.setPattern(pattern);Integer matchStrategy = paramItem.getMatchStrategy();if (!Arrays.asList(PARAM_MATCH_STRATEGY_EXACT, PARAM_MATCH_STRATEGY_CONTAINS, PARAM_MATCH_STRATEGY_REGEX).contains(matchStrategy)) {return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy);}itemEntity.setMatchStrategy(matchStrategy);}} else {entity.setParamItem(null);}// 阈值类型 0-线程数 1-QPSInteger grade = reqVo.getGrade();if (grade == null) {return Result.ofFail(-1, "grade can't be null");}if (!Arrays.asList(FLOW_GRADE_THREAD, FLOW_GRADE_QPS).contains(grade)) {return Result.ofFail(-1, "invalid grade: " + grade);}entity.setGrade(grade);// QPS阈值Double count = reqVo.getCount();if (count == null) {return Result.ofFail(-1, "count can't be null");}if (count < 0) {return Result.ofFail(-1, "count should be at lease zero");}entity.setCount(count);// 间隔Long interval = reqVo.getInterval();if (interval == null) {return Result.ofFail(-1, "interval can't be null");}if (interval <= 0) {return Result.ofFail(-1, "interval should be greater than zero");}entity.setInterval(interval);// 间隔单位Integer intervalUnit = reqVo.getIntervalUnit();if (intervalUnit == null) {return Result.ofFail(-1, "intervalUnit can't be null");}if (!Arrays.asList(INTERVAL_UNIT_SECOND, INTERVAL_UNIT_MINUTE, INTERVAL_UNIT_HOUR, INTERVAL_UNIT_DAY).contains(intervalUnit)) {return Result.ofFail(-1, "Invalid intervalUnit: " + intervalUnit);}entity.setIntervalUnit(intervalUnit);// 流控方式 0-快速失败 2-匀速排队Integer controlBehavior = reqVo.getControlBehavior();if (controlBehavior == null) {return Result.ofFail(-1, "controlBehavior can't be null");}if (!Arrays.asList(CONTROL_BEHAVIOR_DEFAULT, CONTROL_BEHAVIOR_RATE_LIMITER).contains(controlBehavior)) {return Result.ofFail(-1, "invalid controlBehavior: " + controlBehavior);}entity.setControlBehavior(controlBehavior);if (CONTROL_BEHAVIOR_DEFAULT == controlBehavior) {// 0-快速失败, 则Burst size必填Integer burst = reqVo.getBurst();if (burst == null) {return Result.ofFail(-1, "burst can't be null");}if (burst < 0) {return Result.ofFail(-1, "invalid burst: " + burst);}entity.setBurst(burst);} else if (CONTROL_BEHAVIOR_RATE_LIMITER == controlBehavior) {// 2-匀速排队, 则超时时间必填Integer maxQueueingTimeoutMs = reqVo.getMaxQueueingTimeoutMs();if (maxQueueingTimeoutMs == null) {return Result.ofFail(-1, "maxQueueingTimeoutMs can't be null");}if (maxQueueingTimeoutMs < 0) {return Result.ofFail(-1, "invalid maxQueueingTimeoutMs: " + maxQueueingTimeoutMs);}entity.setMaxQueueingTimeoutMs(maxQueueingTimeoutMs);}Date date = new Date();entity.setGmtModified(date);try {entity = repository.save(entity);} catch (Throwable throwable) {logger.error("update gateway flow rule error:", throwable);return Result.ofThrowable(-1, throwable);}if (!publishRules(app, entity.getIp(), entity.getPort())) {logger.warn("publish gateway flow rules fail after update");}return Result.ofSuccess(entity);}@PostMapping("/delete.json")@AuthAction(AuthService.PrivilegeType.DELETE_RULE)public Result<Long> deleteFlowRule(Long id) {if (id == null) {return Result.ofFail(-1, "id can't be null");}GatewayFlowRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofSuccess(null);}try {repository.delete(id);} catch (Throwable throwable) {logger.error("delete gateway flow rule error:", throwable);return Result.ofThrowable(-1, throwable);}if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {logger.warn("publish gateway flow rules fail after delete");}return Result.ofSuccess(id);}private boolean publishRules(String app, String ip, Integer port) {List<GatewayFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));try {publisher.publish(app, rules);return true;} catch (Exception e) {e.printStackTrace();return false;}}
}
5.6. ParamFlowRuleController

修改 com.alibaba.csp.sentinel.dashboard.controller包下的ParamFlowRuleController,内容如下:

package com.alibaba.csp.sentinel.dashboard.controller;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.util.VersionUtils;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;/*** @author Eric Zhao* @since 0.2.1*/
@RestController
@RequestMapping(value = "/paramFlow")
public class ParamFlowRuleController {private final Logger logger = LoggerFactory.getLogger(ParamFlowRuleController.class);//    @Autowired
//    private SentinelApiClient sentinelApiClient;@Autowiredprivate AppManagement appManagement;@Autowiredprivate RuleRepository<ParamFlowRuleEntity, Long> repository;@Autowired@Qualifier("paramRuleNacosProvider")private DynamicRuleProvider<List<ParamFlowRuleEntity>> ruleProvider;@Autowired@Qualifier("paramRuleNacosPublisher")private DynamicRulePublisher<List<ParamFlowRuleEntity>> rulePublisher;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);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());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());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());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 void publishRules(String app, String ip, Integer port) throws Exception {List<ParamFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
//        return sentinelApiClient.setParamFlowRuleOfMachine(app, ip, port, rules);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);
}
5.7. SystemController

修改 com.alibaba.csp.sentinel.dashboard.controller包下的SystemController,内容如下:

package com.alibaba.csp.sentinel.dashboard.controller;import java.util.Date;
import java.util.List;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.util.StringUtil;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.domain.Result;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author leyou(lihao)*/
@RestController
@RequestMapping("/system")
public class SystemController {private final Logger logger = LoggerFactory.getLogger(SystemController.class);@Autowiredprivate RuleRepository<SystemRuleEntity, Long> repository;@Autowiredprivate SentinelApiClient sentinelApiClient;@Autowired@Qualifier("systemRuleNacosProvider")private DynamicRuleProvider<List<SystemRuleEntity>> ruleProvider;@Autowired@Qualifier("systemRuleNacosPublisher")private DynamicRulePublisher<List<SystemRuleEntity>> rulePublisher;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 valueif (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);} 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);} 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);} 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);try {rulePublisher.publish(app, rules);return true;} catch (Exception e) {return false;}}
}
六、前端代码调整
6.1. sidebar.html

修改前端代码
找到webapp/resources/app/scripts/directives/sidebar/sidebar.html文件,找到以下代码:

<li ui-sref-active="active" ng-if="!entry.isGateway"><a ui-sref="dashboard.flowV1({app: entry.app})"><i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
</li>

修改为:

<!-- 修改 dashboard.flowV1 为 dashboard.flow-->
<li ui-sref-active="active" ng-if="!entry.isGateway"><a ui-sref="dashboard.flow({app: entry.app})"><i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
</li>
6.2. flow_service_v1.js

找到webapp/resources/app/scripts/services/flow_service_v1.js文件,找到以下代码:

this.newRule = function (rule) {var param = {resource: rule.resource,limitApp: rule.limitApp,grade: rule.grade,count: rule.count,strategy: rule.strategy,refResource: rule.refResource,controlBehavior: rule.controlBehavior,warmUpPeriodSec: rule.warmUpPeriodSec,maxQueueingTimeMs: rule.maxQueueingTimeMs,app: rule.app,ip: rule.ip,port: rule.port};return $http({url: '/v1/flow/rule',data: rule,method: 'POST'});
};

修改为:

this.newRule = function (rule) {var param = {resource: rule.resource,limitApp: rule.limitApp,grade: rule.grade,count: rule.count,strategy: rule.strategy,refResource: rule.refResource,controlBehavior: rule.controlBehavior,warmUpPeriodSec: rule.warmUpPeriodSec,maxQueueingTimeMs: rule.maxQueueingTimeMs,app: rule.app,ip: rule.ip,port: rule.port};return $http({url: '/v2/flow/rule',data: rule,method: 'POST'});
};

主要是将 url 里的 v1 改为 v2。

6.3. identity.js

找到webapp/resources/app/scripts/controllers/identity.js文件,找到以下代码:

function saveFlowRule() {if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) {return;}FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) {if (data.code === 0) {flowRuleDialog.close();let url = '/dashboard/flow/' + $scope.app;$location.path(url);} else {alert('失败:' + data.msg);}}).error((data, header, config, status) => {alert('未知错误');});
}

修改为:

function saveFlowRule() {if (!FlowService.checkRuleValid(flowRuleDialogScope.currentRule)) {return;}FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) {if (data.code === 0) {flowRuleDialog.close();let url = '/dashboard/v2/flow/' + $scope.app;$location.path(url);} else {alert('失败:' + data.msg);}}).error((data, header, config, status) => {alert('未知错误');});
}

主要是将跳转到 v1 版本的流控界面改为跳转到 v2 版本的流控界面。

6.4. flow_v2.html

找到webapp/resources/app/views/flow_v2.html文件,找到以下代码:

<a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.flowV1({app: app})">回到单机页面
</a>

将其注释掉或删掉。

七、修改的文件总览
7.1. 后端文件列表
路径
sentinel-dashboard/pom.xml依赖
sentinel-dashboard/src/main/resources/application.properties
com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfig

新增部分

路径说明
com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosPropertiesConfig
com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil
com.alibaba.csp.sentinel.dashboard.rule.nacos.auth.AuthorityRuleNacosProvider
com.alibaba.csp.sentinel.dashboard.rule.nacos.auth.AuthorityRuleNacosPublisher
com.alibaba.csp.sentinel.dashboard.rule.nacos.degrade.DegradeRuleNacosProvider
com.alibaba.csp.sentinel.dashboard.rule.nacos.degrade.DegradeRuleNacosPublisher
com.alibaba.csp.sentinel.dashboard.rule.nacos.flow.FlowRuleNacosProvider
com.alibaba.csp.sentinel.dashboard.rule.nacos.flow.FlowRuleNacosPublisher
com.alibaba.csp.sentinel.dashboard.rule.nacos.gateway.api.GatewayApiRuleNacosProvider
com.alibaba.csp.sentinel.dashboard.rule.nacos.gateway.api.GatewayApiRuleNacosPublisher
com.alibaba.csp.sentinel.dashboard.rule.nacos.gateway.flow.GatewayFlowRuleNacosProvider
com.alibaba.csp.sentinel.dashboard.rule.nacos.gateway.flow.GatewayFlowRuleNacosPublisher
com.alibaba.csp.sentinel.dashboard.rule.nacos.param.ParamRuleNacosProvider
com.alibaba.csp.sentinel.dashboard.rule.nacos.param.ParamRuleNacosPublisher
com.alibaba.csp.sentinel.dashboard.rule.nacos.system.SystemRuleNacosProvider
com.alibaba.csp.sentinel.dashboard.rule.nacos.system.SystemRuleNacosPublisher
com.alibaba.csp.sentinel.dashboard.controller.gateway.GatewayApiController
com.alibaba.csp.sentinel.dashboard.controller.gateway.GatewayFlowRuleController

控制层

路径说明
com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2
com.alibaba.csp.sentinel.dashboard.controller.AuthorityRuleController
com.alibaba.csp.sentinel.dashboard.controller.DegradeController
com.alibaba.csp.sentinel.dashboard.controller.ParamFlowRuleController
com.alibaba.csp.sentinel.dashboard.controller.SystemController
7.2. 前端文件列表
路径说明
webapp/resources/app/scripts/directives/sidebar/sidebar.html
webapp/resources/app/scripts/services/flow_service_v1.js
webapp/resources/app/scripts/controllers/identity.js
webapp/resources/app/views/flow_v2.html

Alibaba Sentinel Nacos 持久化规则

至此改造基本完毕。

八、编译打包
8.1. 编译前端

进入项目 webapp/resources 目录,运行

npm install
8.2. 编译打包后端

进入项目 sentienl-dashboard 目录,运行

mvn clean package -Dmaven.test.skip=true

如果报错,则进入上级目录 sentinel ,执行mvn clean package -Dmaven.test.skip=true 后重新打包。
之后会在 target 目录下生成 sentinel-dashboard.jar 。

通过以下命令运行

java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
九、测试验证
9.1. 版本选取
组件版本
nacos1.4.2
sentinel1.8.2
9.2. 启动nacos

在这里插入图片描述
http://localhost:8848/nacos/
账号/密码:nacos/nacos

在这里插入图片描述

9.3. 启动sentinel-dashboard
java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

在这里插入图片描述
http://localhost:8888/

在这里插入图片描述

在这里插入图片描述

9.4. 添加流控规则

在这里插入图片描述
在这里插入图片描述

登录nacos查看规则是否同步
在这里插入图片描述

在这里插入图片描述

9.5. 重启sentinel-dashboard

在未做改造前规则存在内存中,sentinel-dashboard重启后规则失效;查看改造后,sentinel-dashboard重启后,nacos是否将规则同步到sentinel-dashboard控制台的流控规则页面中。
在这里插入图片描述
重新登陆sentinel-dashboard
在这里插入图片描述
重启后规则仍然存在

9.6. 删除流控规则

在sentinel-dashboard控制台的流控规则中将规则删除,查看nacos规则是否同步删除

在这里插入图片描述
流控规则是空的
在这里插入图片描述
登录nacos查看规则,是否同步删除了
在这里插入图片描述
在这里插入图片描述

9.7. nacos删除流控规则

从 nacos删除流控规则 ,验证sentinel-dashboard是否同步删除,其实想验证nacos修改规则参数,sentinel-dashboard控制台是否同步配置。
删除前:
在这里插入图片描述
在这里插入图片描述
删除规则:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
登录sentinel-dashboard查看规则是否还存在
刷新sentinel-dashboard
在这里插入图片描述
发现规则没有,验证效果符合预期
从上面截图中可以看出流控规则删除了后nacos保存得流控规则也同步删除了。

9.8. nacos修改流控规则

在nacos上修改流控规则,验证sentinel-dashboard控制台是否同步
修改前:
在这里插入图片描述
在这里插入图片描述
修改后:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
登录sentinel-dashboard控制台查看流控规则配置是否也同步修改了
在这里插入图片描述
从截图中可以看出,配置同步修改了

9.9. 测试结论

从上面通过在sentinel-dashboard控制台对流控规则的增加、删除以及sentinel-dashboard服务的重启启动,nacos上规则删除、规则修改等方面,测试结果可以看出符合预期,说明改造sentinel-dashboard,使用Nacos持久化规则成功!

十、微服务集成
10.1. pom依赖引入
   <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.2.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!--服务注册发现--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--配置管理--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--配置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></dependencies><dependencyManagement><dependencies><!--spring-cloud-alibaba依赖版本控制--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.6.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement>
10.2. sentinel规则持久化配置
spring:cloud:sentinel:transport:dashboard: 127.0.0.1:8888datasource:# 名称随意flow:nacos:server-addr: localhost:8848dataId: ${spring.application.name}-flow-rulesgroupId: SENTINEL_GROUP# 规则类型,取值见:# org.springframework.cloud.alibaba.sentinel.datasource.RuleTyperule-type: flowdegrade:nacos:server-addr: localhost:8848dataId: ${spring.application.name}-degrade-rulesgroupId: SENTINEL_GROUPrule-type: degradesystem:nacos:server-addr: localhost:8848dataId: ${spring.application.name}-system-rulesgroupId: SENTINEL_GROUPrule-type: systemauthority:nacos:server-addr: localhost:8848dataId: ${spring.application.name}-authority-rulesgroupId: SENTINEL_GROUPrule-type: authorityparam-flow:nacos:server-addr: localhost:8848dataId: ${spring.application.name}-param-flow-rulesgroupId: SENTINEL_GROUPrule-type: param-flownacos:discovery:service: order-servserver-addr: localhost:8848application:name: sentinel-nacosserver:port: 9000management:endpoint:endpoints:web:exposure:include: '*'
10.3. nacos效果图

与上面的配置一一对应
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/517689.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何基于 Nacos 和 Sentinel ,实现灰度路由和流量防护一体化

Nepxion Discovery框架在实现灰度发布和路由功能前提下&#xff0c;结合Nacos和Sentinel&#xff0c;对流量再实施一层防护措施&#xff0c;更能达到企业级的流量安全控制的目的。它的功能包括&#xff1a; 封装远程配置中心和本地规则文件的读取逻辑&#xff0c;即优先读取远…

神操作!一行Python代码搞定一款游戏?给力!

来源&#xff1a;pypl编程榜一直以来Python长期霸占编程语言排行榜前三位&#xff0c;其简洁&#xff0c;功能强大的特性使越来越多的小伙伴开始学习Python 。甚至K12的同学都开始学习Python 编程。新手入门的时候趣味性其实最重要的。那么一行Python 代码到底能玩出什么花样&a…

详解阿里云数据中台,一篇文章全面了解大数据“网红”

一直想写一篇关于数据中台正面文章&#xff0c;现在有闲时做些总结&#xff0c;想充分诠释一下DT内部人如何看待数据中台。 数据中台的概念是最早由阿里巴巴首次提出&#xff0c;是为了应对内部众多业务部门千变万化的数据需求和高速时效性的要求而成长起来的&#xff0c;它既要…

云原生时代,蚂蚁金服公开了新的金融混合云架构

蚂蚁金服在过去十五年重塑支付改变生活&#xff0c;为全球超过十二亿人提供服务&#xff0c;这些背后离不开技术的支撑。在 2019 杭州云栖大会上&#xff0c;蚂蚁金服将十五年来的技术沉淀&#xff0c;以及面向未来的金融技术创新和参会者分享。我们将其中的优秀演讲整理成文并…

Python 薪资降温?不存在的

当你学习编程时&#xff0c;最先被困扰在哪一步&#xff1f;是不是很容易陷入在语法之类的细节而忽视基础概念&#xff1f;解决当前任务的最佳方法是什么&#xff1f;在多种编程语言之间来回切换&#xff0c;却感觉不到效率的提高&#xff1f;0 基础学习编程&#xff0c;最先入…

隐私与AI兼得,蚂蚁金服是如何做到的?

蚂蚁金服在过去十五年重塑支付改变生活&#xff0c;为全球超过十二亿人提供服务&#xff0c;这些背后离不开技术的支撑。在 2019 杭州云栖大会上&#xff0c;蚂蚁金服将十五年来的技术沉淀&#xff0c;以及面向未来的金融技术创新和参会者分享。我们将其中的优秀演讲整理成文并…

Nacos配置中心规范

文章目录一、版本选取和概念理解1. 版本选择2.Namespace3. 如何进行配置和服务的管理、隔离&#xff08;Group&#xff09;二、方案选取1. 命名空间创建2. Namespace实施方案三、nacos配置实战3.1. dev环境配置创建3.2. test环境配置创建四、代码coding实战4.1. 创建2项目4.2. …

干货|Flutter 原理与闲鱼深度实践

王康&#xff08;正物&#xff09;—— Flutter 官方成员 阿里巴巴技术专家&#xff0c;之前主要负责 Flutter 在闲鱼中的混合开发体系&#xff0c;目前重点关注 Flutter 深入度以及生态相关的工作。本文将分享三方面内容&#xff0c; Flutter 的原理、 Flutter 在闲鱼中的应用…

云计算,巨头们的背水一战

作者 | 马超责编 | 伍杏玲头图 | CSDN 下载自视觉中国出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;最近国内各IT巨头技术布局都颇有看点&#xff0c;先是腾讯宣布将投入5000亿&#xff0c;用于新基建的进一步布局&#xff08;将重点投入云计算、人工智能、区块链…

基于函数计算的 Serverless AI 推理

前言概述 本文介绍了使用函数计算部署深度学习 AI 推理的最佳实践, 其中包括使用 FUN 工具一键部署安装第三方依赖、一键部署、本地调试以及压测评估&#xff0c; 全方位展现函数计算的开发敏捷特性、自动弹性伸缩能力、免运维和完善的监控设施。 1.1 DEMO 概述 通过上传一个…

《Flutter in action》开放下载!闲鱼Flutter企业级实践精选

复制链接到浏览器 https://yq.aliyun.com/download/3792?utm_contentg_1000081730 下载。 闲鱼是国内最早使用Flutter的团队&#xff0c;也是Flutter业务线渗入最深的团队之一。 现在承载亿级流量的闲鱼将多年最佳实践经验整理成册&#xff0c;《Flutter in action》 正式面世…

阿里HBase高可用8年抗战回忆录

前言 2011年毕玄和竹庄两位大神将HBase引入阿里技术体系&#xff0c;2014年接力棒转到东8区第一位HBase commiter天梧手中&#xff0c;多年来与淘宝、旺旺、菜鸟、支付宝、高德、大文娱、阿里妈妈等几乎全BU合作伙伴携手共进&#xff0c;支撑了双十一大屏、支付宝账单、支付宝…

nginx 1.9.9 Linux 环境安装

文章目录一、软件下载和安装Nginx相关依赖1. 安装Nginx相关依赖2. 下载Nginx二、源码安装Nginx2.1. 解压2.2. nginx默认配置2.3. 编译安装2.3. 查找安装路径2.4.启动nginx2.5. 查看是否启动成功一、软件下载和安装Nginx相关依赖 1. 安装Nginx相关依赖 yum -y install gcc zli…

Java面向对象部分小结

Java面向对象部分小结 第一天: 1. 了解面向对象和面向过程 2. 对象是什么&#xff0c;静态特性&#xff08;属性&#xff09;&#xff0c;动态特征&#xff08;方法&#xff09; 3. 类和对象的关系 类是抽象的&#xff0c;对象是具体的类是具有相同属性和行为&#xff08;…

音视频应用驶入快车道 开发者如何快速追赶这波技术红利?

受访人 | 融云CPO 任杰 作者 | June 图片来源 | 视觉中国 毋庸置疑&#xff0c;随着5G时代的到来&#xff0c;实时音视频技术将会上升到一个全新的高度。 5G时代发生巨变的远远不止网速&#xff0c;凭借5G网络的高带宽&#xff0c;低延迟和大并发性&#xff0c;音视频应用场…

Mysql 8.0 安装教程 Linux Centos7

文章目录一、软件下载上传1. 下载2. 上传二、软件安装配置2.1. 解压mysql2.2. 创建data文件夹 存储文件2.3. 创建用户组以及用户和密码2.4. 授权用户2.5. 切换到bin目录下2.6. 编辑my.cnf文件2.7. 添加mysqld服务到系统2.8. 授权以及添加服务2.9. 启动mysql2.10. 查看启动状态2…

重磅发布 | 全球首个云原生应用标准定义与架构模型 OAM 正式开源

Kubernetes 项目作为容器编排领域的事实标准&#xff0c; 成功推动了诸如阿里云 Kubernetes &#xff08;ACK&#xff09;等云原生服务的迅速增长。但同时我们也关注到&#xff0c;Kubernetes 的核心 API 资源比如 Service、Deployment 等&#xff0c;实际上只是应用中的不同组…

今天的作业 --- 去重

使用Set的写法: public String myMethod1(String str){ //Set方法Set setnew HashSet();for (int i 0; i < str.length(); i) {set.add(str.charAt(i));}String s "";for (Object o :set) {so;}return s;}由于Set集合内不会存储重复的字符,所以…

2020 AI 产业图谱启动,勾勒中国 AI 技术与行业生态

《2020年国务院政府工作报告》提出&#xff0c;重点支持「两新一重」建设。其中「两新一重」中的第一个「新」&#xff0c;就是新基建&#xff0c;而人工智能是新基建的重要组成部分。新基建首次被纳入政府工作报告后&#xff0c;各大科技厂商纷纷押注&#xff0c;重金投向「新…

从零开始入门 K8s | Kubernetes 网络概念及策略控制

一、Kubernetes 基本网络模型 本文来介绍一下 Kubernetes 对网络模型的一些想法。大家知道 Kubernetes 对于网络具体实现方案&#xff0c;没有什么限制&#xff0c;也没有给出特别好的参考案例。Kubernetes 对一个容器网络是否合格做出了限制&#xff0c;也就是 Kubernetes 的…