前言:
今天朋友们对“sentinel持久化问题”大致比较珍视,兄弟们都想要分析一些“sentinel持久化问题”的相关资讯。那么小编也在网摘上搜集了一些有关“sentinel持久化问题””的相关内容,希望我们能喜欢,兄弟们快快来了解一下吧!#头条创作挑战赛#
终身学习、乐于分享、共同成长!
前言
在前两章中,我们设定的规则(无论是通过代码还是控制台)都只是保存在内存中的,不能在应用重启之后保留。如果规则丢失,我们需要重新设置它们,这非常麻烦。
Sentinel Dashboard是通过transport模块来获取每个Sentinel客户端中的规则,同时通过RuleRepository接口将获取到的规则保存在Dashboard的内存中,如果在Dashboard页面中更改了某个规则,也会调用transport模块提供的接口将规则更新到客户端中去。
原理如下:
试想一下,客户端连接上Dashboard之后,我们在Dashboard上为客户端配置好了规则,并推送给了客户端。这时由于一些因素客户端出现异常,服务不可用了,当客户端恢复正常再次连接上Dashboard后,这时所有的规则都丢失了,我们还需要重新配置一遍规则,这也太不友好了。
Sentinel本身没有提供规则持久化的功能,因此我们需要自己实现。在本章中,我们将介绍如何实现规则持久化。
规则持久化实现原理
实现规则持久化的思路很简单,就是将保存在RuleManager内存中的规则持久化一份副本到存储介质(可以是Nacos、Zookeeper、MySQL、Redis、File)中,当应用重启后再从存储介质中把规则load到内存,这样样规则就不会丢失了。
如下图:
Sentinel提供了两个接口来实现规则的持久化:ReadableDataSource 和 WritableDataSource。
WritableDataSource是用于在客户端扩展写数据源,客户端主动向某个规则中心定期轮询拉取规则,这个规则中心可以DB、文件等,对客户端有一定的入侵性,所以不太推荐这种方式。ReadableDataSource用于在SentinelDashboard扩展读数据源,由规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用Nacos、Zookeeper等配置中心,这种方式有更好的实时性和一致性保证,生产环境一般采用这种方式实现。
看一下ReadableDataSource接口的具体的定义:
public interface ReadableDataSource<S, T> { /** * 将原始数据转换成我们所需的格式 */ T loadConfig() throws Exception; /** * 从数据源中读取原始的数据 */ S readSource() throws Exception; /** * 获取该种数据源的SentinelProperty对象 */ SentinelProperty<T> getProperty(); /** * 关闭数据源 */ void close() throws Exception;}
接口还是比较简单的,只有四个方法,另外 Sentinel 还为我们提供了一个抽象类:AbstractDataSource,该抽象类中实现了两个方法,具体的数据源实现类只需要实现一个readSource方法即可.
public abstract class AbstractDataSource<S, T> implements ReadableDataSource<S, T> { // Converter接口负责转换数据 protected final Converter<S, T> parser; // SentinelProperty接口负责触发PropertyListener的configUpdate方法的回调 protected final SentinelProperty<T> property; public AbstractDataSource(Converter<S, T> parser) { if (parser == null) { throw new IllegalArgumentException("parser can't be null"); } this.parser = parser; this.property = new DynamicSentinelProperty<T>(); } @Override public T loadConfig() throws Exception { return loadConfig(readSource()); } public T loadConfig(S conf) throws Exception { T value = parser.convert(conf); return value; } @Override public SentinelProperty<T> getProperty() { return property; }}
每个具体的DataSource实现类需要做三件事:
实现readSource方法将数据源中的原始数据转换成可以处理的数据S。提供一个Converter来将数据S转换成最终的数据T。将最终的数据T更新到具体的RuleManager中去。
原理如下图:
几个注意点:
规则的持久化配置中心可以是redis、nacos、zk、file等等任何可以持久化的数据源,只要能保证更新规则时,客户端能得到通知即可。规则的更新可以通过 Sentinel Dashboard 也可以通过各个配置中心自己的更新接口来操作。AbstractDataSource 中的 SentinelProperty 持有了一个 PropertyListener 接口,最终更新 RuleManager 中的规则是 PropertyListener 去做的。
目前Sentinel中默认实现了5种规则持久化的方式,分别是:file、redis、nacos、zk和apollo。
#规则推送的三种模式
推送模式
说明
优点
缺点
原始模式
API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource)
简单,无任何依赖
不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境
Pull 模式
扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等
简单,无任何依赖;规则持久化
不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。
Push 模式
扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。
规则持久化;一致性;快速
引入第三方依赖
原始模式
如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中。这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境。
拉模式
pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的WritableDataSourceRegistry 中。
推模式
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。
因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了。
#应用推模式基于Nacos配置中心实现规则持久化
Sentinel支持五种持久化方式,这里以Nacos为例进行演示。虽然Sentinel源码中默认提供了这些方式的实现,但并未默认启用,需手动修改源码以启用支持。
Sentinel源码(opens new window)
把源码下载下来到本地导入IDEA
接下来进行源码改造,步骤如下:
把sentinel-dashboard工程下的pom.xml文件
将sentinel-datasource-nacos依赖的<scope>test</scope>这一行注释掉
<!-- for Nacos rule publisher sample --><dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId></dependency>修改相关类
将整个sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos目录
拷贝到sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/nacos目录
修改NacosConfig类
修改为实际的Nacos地址。
这里以流控规则的持久化为例,修改FlowControllerV1类,其他规则类似
对com.alibaba.csp.sentinel.dashboard.controller.FlowControllerV1修改的地方如下:
注入我们定义好的provider和publisher,即对应的FlowRuleNacosProvider和FlowRuleNacosPublisher。
@Autowired @Qualifier("flowRuleNacosProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider; @Autowired @Qualifier("flowRuleNacosPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;修改rules接口,改为从nacos中读取数据。
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);最后修改publishRules方法
private CompletableFuture<Void> publishRules(String app, String ip, Integer port) {// List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));// return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules); List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); try { rulePublisher.publish(app, rules); logger.info("添加限流规则成功....."); } catch (Exception e) { e.printStackTrace(); logger.info("添加限流规则失败....."); } return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules); }
至此,源码改造就完成了,我们来启动一下DashboardApplication,启动正常就可以了。
接下来配置客户端,客户端项目使用前两张中使用到springcloudalibaba-step-07项目作为演示
在bootstrap.yml配置文件中添加如下配置即可
datasource: flow-rules: ## 流控规则 nacos: server-addr: 127.0.0.1:8848 data-id: ${spring.application.name}-flow-rules group-id: SENTINEL_GROUP data-type: json rule-type: flow
接下来访问Sentinel控制台进行流控规则设置,首次进来是没有客户端的,我们需要访问一次客户端应用的接口,请求一次之后就可以看到客户端了。
我们设置一个流控规则
新增完成后查看Nacos配置列表,这时会看到新增了一个Data Id为sentinel-demo-flow-rules的配置,这个dataid的默认命名规则是{客户端应用名}-flow-rules,当然也可修改代码来自定义这个命名规则。
测试验证一下这个规则是否有效,通过postman快速点击访问接口,1秒访问多次,这时候会出现失败。
至此,Sentinel规则持久化实现完成!
其他规则的实现,比如熔断规则、热点规则、系统规则等都可以参照这个流控规则这个方式来实现,总结下来核心就是provider与publisher,比如熔断规则的持久化实现则新增对应的DegradeRuleNacosProvider与DegradeRuleNacosProvider,实现方式参照流控规则对应provider与publisher的实现。
对于所有类型规则的持久化实现也可以关注我的公众号Seven的代码实验室回复Sentinel获取完整源码。
总结本身分析了Sentinel持久化的实现原理,思路很简单,就是将保存在RuleManager内存中的规则持久化一份副本到存储介质,当应用重启后再从存储介质中把规则load到内存。需要依赖Sentinel提供的两个接口ReadableDataSource 和 WritableDataSource。Sentinel规则推动的三种模式:原始模式、拉模式、推模式,生产环境推荐使用推模式。应用推模式基于Nacos配置中心实现了Sentinel的规则持久化。
标签: #sentinel持久化问题