赞
踩
Sentinel 规则的推送有下面三种模式:
通过前面的讲解,我们已经知道,可以通过 Dashboard 来为每个 Sentinel 客户端设置各种各样的规则,这种属于原始模式。这种模式存在一个问题,就是这些规则默认是存放在内存中的,极不稳定,所以需要将其持久化。
为了达到持久化的目标,我们需要进行改造,改造的方案有两种:本地文件持久化(拉模式)、配置中心持久化(推模式)
拉模式又被称为 pull 模式,它的数据源(如本地文件、RDBMS等)一般是可写入的。本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。以本地文件数据源为例,推送过程如下图所示:
首先 Sentinel 控制台通过API将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。使用 pull模式的数据源时一般不需要对Sentinel控制台进行改造。这种实现方法好处是简单,坏处是无法保证监控数据的一致性。
#数据库配置
spring:
cloud:
sentinel:
eager: true
transport:
port: 9998 #跟控制台交流的端口,随意指定一个未使用的端口即可
dashboard: localhost:8080 #指定控制台服务的地址
filter:
enabled: false
实现 InitFunc 接口,在 init 中处理 DataSource 初始化逻辑,并利用 SPI 机制实现加载。
public class FilePersistence implements InitFunc { @Value("${spring.application.name}") private String applicationName; @Override public void init() throws Exception { //创建规则文件 String ruleDir = System.getProperty("user.home") + "/sentinel-rules/" + applicationName; String flowRulePath = ruleDir + "/flow-rule.json"; String degradeRulePath = ruleDir + "/degrade-rule.json"; String systemRulePath = ruleDir + "/system-rule.json"; String authorityRulePath = ruleDir + "/authority-rule.json"; String paramFlowRulePath = ruleDir + "/param-flow-rule.json"; this.mkdirIfNotExits(ruleDir); this.createFileIfNotExits(flowRulePath); this.createFileIfNotExits(degradeRulePath); this.createFileIfNotExits(systemRulePath); this.createFileIfNotExits(authorityRulePath); this.createFileIfNotExits(paramFlowRulePath); //流控规则 //创建流控规则的可读数据源 ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(flowRulePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>(){})); //将可读数据源注册至 FlowRuleManager,这样当规则文件发生变化时,就会更新规则到内存 FlowRuleManager.register2Property(flowRuleRDS.getProperty()); WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(flowRulePath,this::encodeJson); //将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中. //这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中. WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS); //降级规则 ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(degradeRulePath, source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>(){})); DegradeRuleManager.register2Property(degradeRuleRDS.getProperty()); WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(degradeRulePath,this::encodeJson); WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS); //系统规则 ReadableDataSource<String, List<SystemRule>> systemRuleRDS= new FileRefreshableDataSource<>(systemRulePath, source -> JSON.parseObject(source, new TypeReference<List<SystemRule>>(){})); SystemRuleManager.register2Property(systemRuleRDS.getProperty()); WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(systemRulePath,this::encodeJson); WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS); //授权规则 ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(authorityRulePath, source-> JSON.parseObject(source, new TypeReference<List<AuthorityRule>>(){})); AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty()); WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(authorityRulePath, this::encodeJson); WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS); //热点参数规则 ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(paramFlowRulePath, source-> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>(){})); ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty()); WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(paramFlowRulePath,this::encodeJson); ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS); } }
在resources下创建配置目录 META-INF/services
,然后添加文件com.alibaba.csp.sentinel.init.InitFunc
,在文件中添加配置类的全路径it.aq.cheetah.config.FilePersistence
。
这样当在 Dashboard 中修改了配置后,Dashboard 会调用客户端的接口修改客户端内存中的值,同时将配置写入文件中,这样操作的话规则是实时生效的,如果是直接修改文件中的内容,这样需要等定时任务3秒后执行才能读到最新的规则。接下来我们演示下:
编写测试类
@RestController
@RequestMapping("/product2")
@Slf4j
public class ProductController2 {
@RequestMapping("/test")
@SentinelResource(value = "test")
public String test() {
return "product2";
}
}
启动项目,发现在目录下生成了空的规则文件
在页面上增加流控规则
然后去看文件flow-rule.json
,发现存到了本地文件中
接着我们仿照该规则仿写一个熔断规则,然后查看网页数据确实生效了
推模式又叫 Push 模式,它是通过注册中心实现的,Sentinel控制台——>配置中心——>Sentinel数据源——>Sentinel
用户不仅可以通过sentinel控制台进行更新,也可以通过nacos配置中心进行更新,所以在sentinel控制台或nacos中修改规则后,都需要通知对方刷新最新的配置。
我们在之前项目的基础上引入新的依赖
<!--nacos配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--以nacos作为sentinel数据源的依赖-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
nacos 配置:因为我们用nacos作为了配置中心,我们可以将sentinel的基本配置放入到nacos中就可以了,所以当前服务的yml配置文件中只需要写一些基本的配置就可以了。
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yml
在nacos中配置sentinel信息
spring: cloud: sentinel: transport: # 跟控制台交流的端口,随意指定一个未使用的端口即可 port: 9998 # 指定控制台服务的地址 dashboard: localhost:8080 # sentinel用nacos作为数据源的配置 datasource: #流控管理(这个名称可以自定义) flow-control: # 告诉sentinel用nacos作为数据源 nacos: # 配置中心里执行文件的 dataId dataId: shop-product-flow.json # nacos的地址 serverAddr: 127.0.0.1:8848 # 指定文件配置的是哪种规则 rule-type: flow
注意:如果使用的 namespace 不是默认的,记得配置 namespace 参数。
com.alibaba.cloud.sentinel.datasource
包下的枚举类:RuleType。public enum RuleType { /** * flow. */ FLOW("flow", FlowRule.class), /** * degrade. */ DEGRADE("degrade", DegradeRule.class), /** * param flow. */ PARAM_FLOW("param-flow", ParamFlowRule.class), /** * system. */ SYSTEM("system", SystemRule.class), /** * authority. */ AUTHORITY("authority", AuthorityRule.class), /** * gateway flow. */ GW_FLOW("gw-flow", "com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"), /** * api. */ GW_API_GROUP("gw-api-group", "com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");
shop-product-flow.json
文件中配置【流控规则】
[ { "clusterConfig": { "acquireRefuseStrategy": 0, "clientOfflineTime": 2000, "fallbackToLocalWhenFail": true, "resourceTimeout": 2000, "resourceTimeoutStrategy": 0, "sampleCount": 10, "strategy": 0, "thresholdType": 0, "windowIntervalMs": 1000 }, "clusterMode": false, "controlBehavior": 0, "count": 10.0, "grade": 1, "limitApp": "default", "maxQueueingTimeMs": 500, "resource": "/product2/test", "strategy": 0, "warmUpPeriodSec": 10 } ]
然后去 dashboard 中查看,发现流控规则已经在控制中显示了
目前我们已经实现了在 nacos 中配置的文件直接在sentinel dashboard
中生效,但是我们在sentinel dashboard
中修改了配置,nacos 是不会监听到并进行修改的。接下来我们实现一下通过 sentinel 控制台设置的规则直接持久化到 nacos配置中心。
Sentinel 控制台提供 DynamicRulePublisher
和 DynamicRuleProvider
接口用于实现应用维度的规则推送和拉取。
https://github.com/alibaba/Sentinel/releases
下载dashboard的代码源码。
解压之后打开sentinel-dashboard
项目,将 pom.xml 文件中作用域为 test 的注释掉,注释掉后默认的作用域为 compile。
把 test 包下的两个类复制过来
NacosConfigUtil
类主要就是 nacos 配置的规则,比如配置文件的后缀,分组Group_ID等等。因为我用的分组是默认分组,所以改为DEFAULT_GROUP
,我之前的规则文件是shop-product-flow.json
,所以我把规则文件后缀FLOW_DATA_ID_POSTFIX
改为"-flow.json"。NacosConfig
类是为了注入nacos的信息以及转换器类。application.properties
中增加 nacos 的配置
# nacos 配置
nacos.serverAddr=localhost:8848
nacos.username=nacos
nacos.password=nacos
NacosConfig
修改为从配置文件中获取nacos配置
@Bean
public ConfigService nacosConfigService() throws Exception {
Properties properties = new Properties();
//Nacos地址
properties.put("serverAddr", serverAddr);
//Nacos用户名
properties.put("username", username);
//Nacos密码
properties.put("password", password);
return ConfigFactory.createConfigService(properties);
}
在com.alibaba.csp.sentinel.dashboard.rule.FlowRuleApiPublisher#publish
方法中增加推送到nacos的逻辑代码
//将规则推送到nacos
configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, converter.convert(rules));
在com.alibaba.csp.sentinel.dashboard.rule.FlowRuleApiProvider#getRules
方法中修改为从nacos中读取配置的逻辑
@Override
public List<FlowRuleEntity> getRules(String appName) throws Exception {
String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, 3000);
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
改造流控的controller类FlowControllerV1,将配置保存到内存中的逻辑改为保存到nacos中
@Autowired @Qualifier("flowRuleDefaultProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider; @Autowired @Qualifier("flowRuleDefaultPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher; @GetMapping("/rules") @AuthAction(PrivilegeType.READ_RULE) public Result<List<FlowRuleEntity>> apiQueryMachineRules(@RequestParam String app, @RequestParam String ip, @RequestParam Integer port) { ...... try { // List<FlowRuleEntity> rules = sentinelApiClient.fetchFlowRuleOfMachine(app, ip, port); //从nacos中读取规则 List<FlowRuleEntity> rules = ruleProvider.getRules(app); 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 void publishRules(String app, String ip, Integer port) throws Exception { //将规则推送到nacos List<FlowRuleEntity> rules = repository.findAllByApp(app); rulePublisher.publish(app, rules); // List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); // return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules); } //其余调用publishRules方法的地方做下简单调整
启动当前项目,流控规则中存在我们之前在nacos中创建的文件,我们将原来的单机阈值从10改为12,然后保存。查看nacos中配置文件的数据,发现已经生效了。
至于其他规则,大家可以自行实现,此处就不一一实现了
到这儿,服务容错中间件Sentinel的两种持久化模式就已经介绍完了。下一篇将为大家带来Feign整合容错组件 Sentinel 的文章,敬请期待吧!
后续的文章,我们将继续完善我们的微服务系统,集成更多的Alibaba组件。想要了解更多JAVA后端知识,请点击文末名片与我交流吧。留下您的一键三连,让我们在这个寒冷的东西互相温暖吧!
参考链接:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。