文章目录
- 一、前言
- 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> 流控规则</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> 流控规则</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. 版本选取
组件 | 版本 |
---|---|
nacos | 1.4.2 |
sentinel | 1.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效果图
与上面的配置一一对应