总系列目录地址
上篇Spring-Cloud插件
猜想soul是如何加装一个插件的
-
通过最简单的http插件来跟踪
尝试soul-bootstrap的其他所有插件依赖都注释掉。<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- soul gateway start --> <dependency> <groupId>org.dromara</groupId> <artifactId>soul-spring-boot-starter-gateway</artifactId> <version>${project.version}</version> </dependency> <!-- if you use http proxy start this--> <dependency> <groupId>org.dromara</groupId> <artifactId>soul-spring-boot-starter-plugin-divide</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.dromara</groupId> <artifactId>soul-spring-boot-starter-sync-data-websocket</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.dromara</groupId> <artifactId>soul-spring-boot-starter-plugin-httpclient</artifactId> <version>${project.version}</version> </dependency>
-
重启soul-bootstrap后查看日志
-
这次的目标是divide插件
查看日志发现,divide插件对应的类:org.dromara.soul.plugin.divide.DividePlugin
不过我们maven加载的依赖是:soul-spring-boot-starter-plugin-divide,先从sarter开始debug, 从divide的starter中可以看到,具体的实现在:soul-plugin-divide
从这里,我们可以学习到,通过外部引入依赖来减少starter的复杂度。
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-plugin-divide</artifactId>
<version>${project.version}</version>
</dependency>
4. 通过SPI机制加载,自动装载bean
@Bean
public SoulPlugin dividePlugin() {
return new DividePlugin();
}
DividePlugin实现了接口SouldPlugin,查看其它starter同样实现了SoulPlugin
@Bean
public SoulPlugin contextPathMappingPlugin() {
return new ContextPathMappingPlugin();
}
从这里我们大概了解到,所有插件都是通过starter实现SoulPlugin接口。在之前的日志中知道,SoulConfiguration会按顺序加载插件,接下来去查看一下具体如何加载。
SoulConfiguration在soul-web里面,是在soul-spring-boot-starter-gateway里面添加的依赖。
@Bean("webHandler")
public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) {
List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
final List<SoulPlugin> soulPlugins = pluginList.stream()
.sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());
soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName()));
return new SoulWebHandler(soulPlugins);
}
通过soulWebHandler 加载所有相关SoulPlugin实现
debug测试
-
查看dividePlugin,是通过责任链方式访问,那么第一个访问入口是哪里呢?
-
通过追踪堆栈,发现第一个入口是SoulWebHandler
-
执行责任链,折行顺序按照 soulConfiguration 里面的加载顺序,然后就一个一个execute执行,直到找到对应的实现。
SoulWebHandler.execute,每次调用责任链都会执行以下方法。方法体应该是使用响应式编程,暂时看不懂。
@Override
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();
});
}
总结
简单来说,soul网关通过spi方式整合不同的协议。为了各种协议不冲突,采用责任链模式,单独判断互不影响。另外通过响应式编程,自动地处理很多底层细节。我自己需要补一补响应式编程的东西,才能更深入理解soul。