3.1.2 Java SPI在Sentinel中的应用
Sentinel使用Java SPI为我们提供了插件注册的功能,类似于Spring Boot提供的自动配置类注册功能。
我们可以直接替换Sentinel提供的默认SlotChainBuilder,使用自定义的SlotChainBuilder为资源构造自己的ProcessorSlotChain,以实现修改ProcessorSlot排列顺序、增加或移除ProcessorSlot的功能。
提示:在Sentinel 1.7.2版本中,Sentinel支持使用SPI注册ProcessorSlot,并且支持排序。
在sentinel-core模块的resources资源目录下,有一个META-INF/services目录,该目录下有两个以接口全类名命名的文件。其中,com.alibaba.csp.sentinel.slotchain.SlotChainBuilder文件用于配置SlotChainBuilder接口的实现类,而com.alibaba.csp.sentinel.init.InitFunc文件用于配置InitFunc接口的实现类,并且这两个配置文件中都配置了接口的默认实现类,如果不添加新的配置,Sentinel将使用默认配置的接口实现类。
• com.alibaba.csp.sentinel.slotchain.SlotChainBuilder文件的默认配置如下。
• com.alibaba.csp.sentinel.init.InitFunc文件的默认配置如下。
ServiceLoader可加载接口配置文件中配置的所有实现类,并且使用反射创建对象,但是是否全部加载及实例化仍然由使用者自己决定。
sentinel-core模块在使用Java SPI机制加载InitFunc与SlotChainBuilder时,会在实现上稍有不同。如果InitFunc接口的配置文件注册了多个实现类,那么这些注册的InitFunc实现类都会被Sentinel加载并实例化,且都会被使用。但是如果SlotChainBuilder接口的配置文件注册了多个实现类,那么Sentinel只会加载和使用第一个实现类。
Sentinel在加载SlotChainBuilder时,只会获取第一个非默认实现类的实例,如果接口配置文件中只有默认实现类而没有注册其他的实现类,那么Sentinel会使用这个默认的SlotChainBuilder。实现源码在SpiLoader的loadFirstInstanceOrDefault方法中,代码如下。
① 获取接口的ServiceLoader实例。
② 遍历加载接口的实现类并获取实例,若获取的实例类型与指定的默认实现类不同,则使用该实例。
③ 使用默认实现类的实例,使用反射创建默认实现类的实例。
因为Sentinel允许存在多个初始化方法,所以Sentinel加载InitFunc与SlotChainBuilder的方式会有所不同。Sentinel使用ServiceLoader加载注册的InitFunc实现类实例的代码如下。
① 遍历获取注册的所有接口实现类的实例。
② 实现排序及包装实例。
③ 遍历调用每个InitFunc实例的初始化方法。
InitFunc可用于初始化配置限流、熔断规则,但在Web项目中基本不会使用它,更多的是先通过监听Spring容器刷新完成事件,再初始化Sentinel配置规则。如果使用Sentinel提供的动态数据源还可以在监听到动态配置改变事件时重新加载规则,则基本使用不到InitFunc。
虽然InitFunc接口与SlotChainBuilder接口的配置文件在sentinel-core模块下,但是并不需要修改Sentinel的源码,也不需要修改sentinel-core模块下的接口配置文件,而只需要在当前项目的resource/META-INF/services目录下创建一个与接口名称相同的配置文件,并在配置文件中添加接口的实现类即可。ServiceLoader会遍历项目依赖的每个jar包下的与接口名称相同的配置文件。
ServiceLoader会遍历项目依赖的每个jar包下的与接口名称相同的配置文件,同一个文件中注册的实现类是按注册顺序遍历的,但多个文件的遍历顺序则是不确定的,所以不要依赖注册顺序实现排序。