Soul网关源码分析-16期(未完)
程序员文章站
2022-06-03 20:23:01
...
文章目录
SOUL 中 SPI 的使用
在之前分析 divide 插件的负载均衡策略时, 有看到过一行代码:
DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip);
当时很简单的略过了它的实现, 它的作用很容易分析, 调用一个看似工具类的方法, 传入多个节点组成的集群, 返回一个节点. 这是一个负载均衡器.
但是细节却非常多, 最重要的一点是使用 SPI 来选择具体的实现类. 看看这个方法的代码:
public class LoadBalanceUtils {
public static DivideUpstream selector(final List<DivideUpstream> upstreamList, final String algorithm, final String ip) {
// 调用自定义的 SPI 得到一个子类
LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm);
return loadBalance.select(upstreamList, ip);
}
}
后面的是调用具体子类的 select()
方法, 根据子类的不同实现, 最终会表现出各种形式. 目前的子类实现有:
- HashLoadBalance
- RandomLoadBalance
- RoundRobinLoadBalance
关键就在于 ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm);
这行.
在研究它之前, 我们先不妨研究下 Java 提供的 SPI 机制.
Java SPI
<<高可用可伸缩微服务架构>> 第3章 Apache Dubbo 框架的原理与实现 中有这样的一句定义.
SPI 全称为 Service Provider Interface, 是 JDK 内置的一种服务提供发现功能, 一种动态替换发现的机制. 举个例子, 要想在运行时动态地给一个接口添加实现, 只需要添加一个实现即可.
书中也有个非常形象的脑图, 展示了 SPI 的使用:
也就是说在我们代码中的实现里, 无需去写入一个 Factory 工厂, 用 MAP 去包装一些子类, 最终返回的类型是父接口. 只需要定义好资源文件, 让父接口与它的子类在文件中写明, 即可通过设置好的方式拿到所有定义的子类对象:
ServiceLoader<Interface> loaders = ServiceLoader.load(Interface.class)
for(Interface interface : loaders){
System.out.println(interface.toString());
}
这种方式相比与普通的工厂模式, 肯定是更符合开闭原则, 新加入一个子类不用去修改工厂方法, 而是编辑资源文件.