soul的插件链
前两天分析 divide 插件的时候,留了个坑,今天就来讲讲 soul 的插件链,把这个坑补上。
我们先看下下面这段代码,已经在前面的文章中出现了几次了,今天就来具体分析分析。
// SoulWebHandler.java
public Mono<Void> execute(final ServerWebExchange exchange) {
return Mono.defer(() -> {
if (this.index < plugins.size()) {
SoulPlugin plugin = plugins.get(this.index++);
Boolean skip = plugin.skip(exchange);
if (skip) {
return this.execute(exchange);
}
return plugin.execute(exchange, this);
}
return Mono.empty();
});
}
这段代码往上找,是 SoulWebHandler#handle
我们发现 SoulWebHandler 实现了 WebHandler
WebHandler 又是 WebFlux 用来处理 web 请求的。(这里再挖一个坑,后面有机会写写 WebFlux 异步编程相关的东西)
soul 实现了 WebHandler ,说明它想用自己的 SoulWebHandler 来处理 web 请求。
然后 SoulWebHandler 里面有个属性 plugins 插件列表,是初始化的时候赋值的,我们接着往上找赋值的地方。
// SoulConfiguration.java
@Bean("webHandler")
public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) {
...
}
找到了 SoulConfiguration ,它本身配置了 @Configuration ,说明它是一个配置类,在启动之后会被加载。然后它里面配置了 name = “webHandler” 的 bean 。它在初始化的时候,会自动加载所有的插件。
ObjectProvider<List<SoulPlugin>> plugins
我们前面分析过,ObjectProvider 是 spring4.7之后推出来的新功能,有了它,可以不需要再写 @Autowired,然后就算参数为空,也不会报错,它会自动加载所有类型是 SoulPlugin 的bean。
接下来,我们看下 soul 的插件代码,发现 SoulPlugin 是一个接口,它里面有下面4个方法。
// SoulPlugin.java
// 通过插件链执行对应插件对请求的处理
Mono<Void> execute(ServerWebExchange exchange, SoulPluginChain chain);
// 获取插件的排序
int getOrder();
// 获取插件的名字
default String named() {
return "";
}
// 判断插件是否需要跳过
default Boolean skip(ServerWebExchange exchange) {
return false;
}
然后 AbstractSoulPlugin 实现了 SoulPlugin ,DividePlugin、WebSocketPlugin 等插件又继承了 AbstractSoulPlugin 。
具体类图如下:(idea里查看类图小技巧,接口下右键Diagrams->Show Diagram,选中接口右键Show Implementations)
可以看到,soul 里面的插件都继承了 AbstractSoulPlugin ,这样网关在启动的时候,就会加载所有 SoulPlugin 的子类,然后根据 order 排序。
// SoulConfiguration#soulWebHandler
final List<SoulPlugin> soulPlugins = pluginList.stream()
.sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());
而order的排序是在 PluginEnum 枚举代码里写死的。
枚举里定义的插件排序如下:
当一个请求过来之后,会调用 SoulWebHandler#handle
public Mono<Void> handle(@NonNull final ServerWebExchange exchange) {
// 监控相关
MetricsTrackerFacade.getInstance().counterInc(MetricsLabelEnum.REQUEST_TOTAL.getName());
Optional<HistogramMetricsTrackerDelegate> startTimer = MetricsTrackerFacade.getInstance().histogramStartTimer(MetricsLabelEnum.REQUEST_LATENCY.getName());
// 新建一个插件链执行插件处理
return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler)
.doOnSuccess(t -> startTimer.ifPresent(time -> MetricsTrackerFacade.getInstance().histogramObserveDuration(time)));
}
然后请求就会根据初始化排好序的插件列表,循环遍历,是否需要跳过这个插件,是否需要执行插件里的 execute 逻辑。
启动 bootstrap ,把断点打在 SoulWebHandler#SoulWebHandler ,看到插件链如下:
我们发现加载的插件和定义的插件对不上,很简单,因为有些插件我们没有在 pom 文件里添加依赖。
总结
soul 通过所有插件实现 SoulPlugin 来初始化的时候加载插件列表,然后通过定义好的 order 来指定插件的排序,形成插件链,通过固定排序来实现插件之间的通信。