1.前言
当项目上Sentinel Dashboard做流量监控的时候,我们可以通过Sentinel控制台修改限流配置,但当我们使用Nacos作为配置中心动态配置流控规则的时候,问题就来了。
首先我们要明白,Sentinel Dashboard的配置是从机器的内存中加载的,如果使用Nacos、Apollo、Zookeeper等作为我们动态加载限流规则的配置中心的时候,我们的配置信息流向是这样的:
就是说客户端从配置中心获取配置,Sentinel Dashboard又从客户端机器的内存中获取配置。
在这种情况下,Sentinel Dashboard可以修改配置,Nacos也可以修改配置,这就会存在数据一致性问题,我们可以从两方面考虑
1)Nacos修改了配置,Sentinel Dashboard会同步更新吗?答案是肯定的,因为Nacos配置变更会通知客户端,客户端配置变更后也会通知Sentinel Dashboard
2)如果在Sentinel Dashboard修改了配置会通知Nacos吗?答案是不会。但是可以通过修改源码实现。
Sentinel控制台在做流控规则的时候,都会调用Sentinel-Dashboard源码中的 一个名叫:FlowControllerV1的接口类,因为流控规则的所有操作,调用的 接口都是如图所示:
这个路径对应的就是FlowControllerV1这个类,但是同时还存在一个 FlowControllerV2的接口类,这个类主要提供的是流控规则的CURD,这两者有啥区别呢?
区别在于:V2可以实现指定数据源的规则拉取和发布,这也就意味着Sentinel-Dashboard可以从Nacos等配置中心进行相互通信,从而实现数据一致性,Sentinel开发者已经做好封装,我们改一下源码配置一下就好了(后面会有教程)。这样我们的配置关系就变成了了这样:
2.实战:Sentinel Dashboard集成Nacos
第一步:下载Sentinel源码,本案例用的是Sentinel-v1.8.6
Sentinel官网下载地址:
https://github.com/alibaba/Sentinel/releases
下载好之后,解压,import project,可以看到sentinel-dashboard就是我们要改动的spring-boot项目
第二步:将pom.xml中的sentinel-datasource-nacos的scope去掉
第三步:进入resources/app/scripts/directives/sidebar/sidebar.html,全局搜一下“dashboard.flowV1”,替换成"dashboard.flow",也就是将V1去除:去除之后就 会调用FlowControllerV2中的CURD的接口
其实Sentinel的开发者已经做好相关的适配,如下图,可以支持集成Apollo、Nacos、Zeekeeper,我们现在是以Nacos为例,进入src/test/java/com.alibaba.csp.sentinel.dashboard.rule.nacos目录,将整个nacos目录及其相关文件复制到src/main/java/com.alibaba.csp.sentinel.dashboard.rule.nacos
注:代码的红叉是因为我复制过去了,相同包名相同class导致的,不必在意,实在强迫症的话,可以把test里面的这几个删掉就好了,不删也不影响运行
复制过来之后,如下图:
其中NacosConfigUtil就一些常量,我自定义了些常量并改名为NacosConfigConstant了,不在意细节
1.NacosConfigConstant.java
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
public class NacosConfigConstant {
public static final String DATA_ID_POSTFIX="-flow-rules";
public static final String GROUP_ID="SENTINEL_GROUP";
}
2.配置nacos相关配置
2.1: application.properties添加nacos配置:
#config my nacos
sentinel.nacos.serverAddr=192.168.1.105:8848
sentinel.nacos.namespace=
sentinel.nacos.groupId=SENTINEL_GROUP
2.2: 新建Nacos配置类NacosPropertiesConfiguration.java
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "sentinel.nacos")
public class NacosPropertiesConfiguration {
private String serverAddr;
private String dataId;
private String groupId;
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;
}
}
3.FlowRuleNacosProvider.java,稍微改动了一下,如下:
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.utils.StringUtils;
@Service("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
@Autowired
private NacosPropertiesConfiguration nacosPropertiesConfiguration;
@Autowired
private ConfigService configService;
@Autowired
private Converter<String, List<FlowRuleEntity>> converter;
/**
* @Description: 通过ConfigService.getConfig方法从NacosConfigServer中读取指定配置信息,通过converter转化为FlowRule规则
* @Author: LiJianhong
* @Param: [appName]
* @Return: java.util.List<com.alibaba.csp.sentinel.dashboard.datasource.entity.
* rule.FlowRuleEntity>
*/
@Override
public List<FlowRuleEntity> getRules(String appName) throws Exception {
String dataId = appName + NacosConfigConstant.DATA_ID_POSTFIX; //user-service-flow-rules
String groupId = nacosPropertiesConfiguration.getGroupId();
String rules = configService.getConfig(dataId, groupId, 3000);
if (StringUtils.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
4.FlowRuleNacosPublisher.java,也是稍微改动了一下:
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
@Service("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
@Autowired
private NacosPropertiesConfiguration nacosPropertiesConfiguration;
@Autowired
private ConfigService configService;
@Autowired
private Converter<List<FlowRuleEntity>, String> converter;
@Override
public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "appName connot be empty");
if (rules == null) {
return;
}
String dataId = new StringBuilder(app).append(NacosConfigConstant.DATA_ID_POSTFIX).toString();
configService.publishConfig(dataId, nacosPropertiesConfiguration.getGroupId(), converter.convert(rules));
}
}
5. 修改com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2,将注入的bean改成我们改过之后的bean
6.最后mvn clean package打包运行jar即可。
接下来是测试环节:
运行改动后的sentinel-dashboard
首先,我在Nacos配置中心配置了dataId=user-service-flow-rules限流规则,如下:
[
{
"app": "user-service",
"clusterConfig": {
"acquireRefuseStrategy": 0,
"clientOfflineTime": 2000,
"fallbackToLocalWhenFail": true,
"flowId": 1001,
"resourceTimeout": 2000,
"resourceTimeoutStrategy": 0,
"sampleCount": 10,
"strategy": 0,
"thresholdType": 1,
"windowIntervalMs": 1000
},
"clusterMode": true,
"controlBehavior": 0,
"count": 30.0,
"gmtModified": 1690385332131,
"grade": 1,
"id": 1001,
"limitApp": "default",
"resource": "com.lee.demo.dubbo.demo.user.ISentinelService",
"strategy": 0
},
{
"app": "user-service",
"clusterConfig": {
"acquireRefuseStrategy": 0,
"clientOfflineTime": 2000,
"fallbackToLocalWhenFail": true,
"flowId": 1002,
"resourceTimeout": 2000,
"resourceTimeoutStrategy": 0,
"sampleCount": 10,
"strategy": 0,
"thresholdType": 0,
"windowIntervalMs": 1000
},
"clusterMode": true,
"controlBehavior": 0,
"count": 10.0,
"gmtModified": 1690373927810,
"grade": 1,
"id": 1002,
"limitApp": "default",
"resource": "com.lee.demo.dubbo.demo.user.IHelloService",
"strategy": 0
}
]
com.lee.demo.dubbo.demo.user.ISentinelService的count配置的是30
com.lee.demo.dubbo.demo.user.IHelloService的count配置的是10
然后看下Sentinel-Dashboard的限流规则是与Nacos一致的:
1)在Nacos修改配置查看Sentinel-Dashboard是否更新配置:
可以看到Sentinel-Dashboard的配置是更新了
2)在Sentinel-Dashboard更新配置,检查是否同步到Nacos,这一步才是验证我们这么辛苦改代码的关键
可以看到,Nacos成功同步了Sentinel-Dashboard的配置修改
注:我发现改了之后,在Sentinel-Dashboard对配置的CRUD之后,页面没有刷新,要我们手动刷新一下才会看到最新数据~~o(>_<)o ~~,这个我没有过多去排查前端的异常,毕竟我只是后端开发嘛。
至此,Sentinel-Dashboard集成Nacos案例演示完毕。
最后分享一下集成Nacos过程中遇到的问题:
1)Sentinel-Dashboard控制台可以新增限流规则同步Nacos,但是“修改”配置的时候弹出警告“失败”,没提示啥原因,经过debug才发现,是因为新增和修改都需要进行限流规则的参数校验,校验的必要参数如下:文章来源:https://www.toymoban.com/news/detail-609553.html
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;
}
因此我把Nacos配置中心的限流规则根据这个必要参数都配置了一下之后,再从Sentinel Dashboard修改配置成功了。文章来源地址https://www.toymoban.com/news/detail-609553.html
到了这里,关于Sentinel Dashboard集成Nacos的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!