微服务高并发:动态配置的实现原理搞懂了
动态配置的实现原理
sentinel-datasource-extension 模块是我们主要研究的模块,这是 Sentinel 实现动态数据源的核心,定义了动态数据源的接口并提供了抽象类。
SentinelProperty
SentinelProperty 是 Sentinel 提供的一个接口,可被注册到 Sentinel 提供的各种规则的 Manager(FlowRuleManager)中。SentinelProperty 可以给 SentinelProperty 添加监听器,在配置改变时,用户可以调用 SentinelProperty#updateValue 方法,由该方法负责调用监听器去更新规则,而不需要调用 FlowRuleManager#loadRules 方法。同时,用户可以注册额外的监听器,在配置改变时做一些其他的事情。
SentinelProperty 并不是在 sentinel-datasource-extension 模块中定义的接口,而是在 sentinelcore 模块中定义的接口,其源码如下:
addListener:添加监听器。
removeListener:移除监听器。
updateValue:通知所有监听器配置已更新,newValue 参数为新的配置。
SentinelProperty 默认使用的实现类是 DynamicSentinelProperty,其实现源码如下:
从上述源码中可以看出,DynamicSentinelProperty 使用 Set 存储已注册的监听器;updateValue 方法负责通知所有监听器,调用监听器的 configUpdate 方法。
在前面分析 FlowRuleManager 时,我们只关注了其 loadRulesAPI,除了可以使用 loadRules API 加载规则配置,FlowRuleManager 还提供了 registerProperty API,用于注册一个 SentinelProperty 实例,以实现动态加载或更新规则配置。
使用 SentinelProperty 实现加载或更新限流规则配置的步骤如下:
给 FlowRuleManager 注册一个 SentinelProperty 实例,替换 FlowRuleManager 默认创建的 SentinelProperty 实例(因为外部拿不到默认的 SentinelProperty)。
这一步由 FlowRuleManager 完成,FlowRuleManager 会给 SentinelProperty 实例注册 FlowPropertyListener 监听器,该监听器负责更新 FlowRuleManager.flowRules 缓存的限流规则配置。
在应用启动或者规则配置改变时,只需要调用 SentinelProperty#updateValue 方法,由 updateValue 通知 FlowPropertyListener 监听器去更新限流规则配置。
FlowRuleManager 支持使用 SentinelProperty 加载或更新限流规则配置的实现源码如下:
LISTENER:PropertyListener 接口的实现类实例,用于监听规则配置更新,在规则配置更新时更新 flowRules 字段缓存的限流规则配置。
currentProperty:当前使用的 SentinelProperty 实例,默认会创建一个 DynamicSentinelProperty 实例。
register2Property:用于注册 SentinelProperty 实例。
实现更新缓存的限流规则配置的 FlowPropertyListener 是 FlowRuleManager 的一个内部类,其源码如下:
PropertyListener 接口定义了如下两个方法:
configUpdate:在更新规则配置时被调用,被调用的时机是 SentinelProperty 的 updateValue 方法被调用时。
configLoad:在首次加载规则配置时被调用,是否被调用由 SentinelProperty 的实现类决定。DynamicSentinelProperty 没有调用这个方法。
所以,现在有如下两种方法可用于更新限流规则配置:
调用 FlowRuleManager#loadRules 方法。
给 FlowRuleManager 注册一个 SentinelProperty 实例,并调用 SentinelProperty 实例的 updateValue 方法。
ReadableDataSource
Sentinel 将读和写数据源抽离成两个接口(早期版本只有读接口,写接口是之后的版本才添加的功能),目前来看,写接口只在热点参数限流模块中被使用。事实上,使用读接口就可以满足我们的需求。
ReadableDataSource 接口的定义如下:
ReadableDataSource 是一个泛型接口,参数化类型 S 代表用于装载从数据源中读取的配置的类型,参数化类型 T 代表对应 Sentinel 中的规则类型。例如,我们可以定义一个 FlowRuleProps 类,用于装载从 yml 配置文件中读取的限流规则配置,然后将 FlowRuleProps 实例转换为 FlowRule 实例,所以 S 可以被替换为 FlowRuleProps,T 可以被替换为 List<FlowRule>。
ReadableDataSource 接口定义的方法说明如下:
loadConfig:加载配置。
readSource:从数据源中读取配置,数据源既可以是 yaml 配置文件,也可以是 MySQL、Redis 等,由实现类决定从哪种数据源中读取配置。
getProperty:获取 SentinelProperty 实例。
close:用于关闭数据源,例如,在使用文件存储配置时,可以在此方法中实现关闭文件输入流功能。
如果动态数据源提供 SentinelProperty,则可以调用 getProperty 方法获取动态数据源提供的 SentinelProperty 实例,并将此 SentinelProperty 实例注册给规则管理器,这样动态数据源在读取到配置时就可以调用自身 SentinelProperty 实例的 updateValue 方法通知规则管理器更新规则。
AbstractDataSource 是一个抽象类,该类可实现 ReadableDataSource 接口,用于简化具体动态数据源的实现,其子类只需要继承 AbstractDataSource 抽象类并实现 readSource 方法即可。
AbstractDataSource 抽象类的源码如下:
AbstractDataSource 抽象类要求所有子类都必须提供一个数据转换器(Converter)。
Converter 用于将 S 类型的实例转换为 T 类型的实例,如将用于装载 yaml 配置的 FlowRuleProps 实例转换为 FlowRule 集合。
AbstractDataSource 实例会在构造方法中创建 DynamicSentinelProperty 实例,因此子类无须创建 SentinelProperty 实例。
AbstractDataSource 实例实现 loadConfig 方法,首先调用子类实现的 readSource 方法从数据源中读取配置,返回的实例类型为 S,然后调用 Converter 实例的 convert 方法,将 S 类型的实例转换为 T 类型的实例。
Converter 接口的定义如下:
convert:将类型为 S 的参数 source 转换为类型为 T 的实例。
评论