soul 源码分析 —— 插件机制

插件原理图

在这里插入图片描述

插件就是一个一个的功能模块,比如:
monitor:监控请求模块
divide:支持http的请求转发
dubbo:支持rpc的请求转发
sign:请求进来的签名验证
hystrix:限流降级插件
等等…
插件是soul核心处理机制,每当一次请求进来时,Soul 通过责任链模式,依次执行责任链上的所有插件,完成请求的处理。
插件的特性:支持热插拨、可扩展

插件加载

网关启动的时候,插件列表被加载到内存
加载到内存的插件为:
在这里插入图片描述
这些插件是怎么加载的呢?
插件默认是在soul-bootstrap中配置的:

 <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-plugin-alibaba-dubbo</artifactId>
     <version>${project.version}</version>
</dependency>
<dependency>
      <groupId>org.dromara</groupId>
      <artifactId>soul-spring-boot-starter-plugin-ratelimiter</artifactId>
      <version>${project.version}</version>
</dependency>
<dependency>
     <groupId>org.dromara</groupId>
     <artifactId>soul-spring-boot-starter-plugin-hystrix</artifactId>
     <version>${project.version}</version>
</dependency>
<dependency>
     <groupId>org.dromara</groupId>
     <artifactId>soul-spring-boot-starter-plugin-waf</artifactId>
     <version>${project.version}</version>
</dependency>
<dependency>
      <groupId>org.dromara</groupId>
      <artifactId>soul-spring-boot-starter-plugin-sign</artifactId>
      <version>${project.version}</version>
</dependency>
<dependency>
      <groupId>org.dromara</groupId>
      <artifactId>soul-spring-boot-starter-plugin-httpclient</artifactId>
      <version>${project.version}</version>
</dependency>
<dependency>
      <groupId>org.dromara</groupId>
      <artifactId>soul-spring-boot-starter-plugin-resilience4j</artifactId>
      <version>${project.version}</version>
</dependency>

soul-web网关核心程序中配置了global插件

<dependency>
       <groupId>org.dromara</groupId>
       <artifactId>soul-spring-boot-starter-plugin-global</artifactId>
       <version>${project.version}</version>
</dependency>

然后应用启动是,通过各个载入的starter插件的Configuration,在插件配置类中,实例化插件到容器
在这里插入图片描述
上图只是列举了GlobalPlugin,其它14个插件都是通过各自的配置类,实例化到容器中。
最后通过SoulConfiguration配置类,将插件列表加入到SoulWebHandler中:
通过(final ObjectProvider<List<SoulPlugin>> plugins),将所有继承了SoulPlugin的 并且已经 加载到容器的插件注入进来

public class SoulConfiguration {
    
    /**
     * Init SoulWebHandler.
     *
     * @param plugins this plugins is All impl SoulPlugin.
     * @return {@linkplain SoulWebHandler}
     */
    @Bean("webHandler")
    public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) {
        // 获取所有可用插件
        List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
        // 把插件按照配置order进行排序
        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()));
        // 初始化SoulWebHandler类
        return new SoulWebHandler(soulPlugins);
    }
    ......
}

public final class SoulWebHandler implements WebHandler {

    private final List<SoulPlugin> plugins;

    private final Scheduler scheduler;

    /**
     * Instantiates a new Soul web handler.
     *
     * @param plugins the plugins
     */
    public SoulWebHandler(final List<SoulPlugin> plugins) {
        // 初始化插件
        this.plugins = plugins;
        // TODO:线程调度相关
        String schedulerType = System.getProperty("soul.scheduler.type", "fixed");
        if (Objects.equals(schedulerType, "fixed")) {
            int threads = Integer.parseInt(System.getProperty(
                    "soul.work.threads", "" + Math.max((Runtime.getRuntime().availableProcessors() << 1) + 1, 16)));
            scheduler = Schedulers.newParallel("soul-work-threads", threads);
        } else {
            scheduler = Schedulers.elastic();
        }
    }
    ......
}

至此,

0 = {GlobalPlugin@7938}
1 = {SignPlugin@10092}
2 = {WafPlugin@10093}
3 = {RateLimiterPlugin@10094}
4 = {HystrixPlugin@10095}
5 = {Resilience4JPlugin@10096}
6 = {DividePlugin@7993}
7 = {WebClientPlugin@9890}
8 = {WebSocketPlugin@10097}
9 = {BodyParamPlugin@10098}
10 = {AlibabaDubboPlugin@10099}
11 = {MonitorPlugin@10100}
12 = {DubboResponsePlugin@10101}
13 = {WebClientResponsePlugin@9998}

动态地把所有需要的插件载入到了SoulWebHandler(soul网关web请求调度器中)的plugins属性中。
而被载入的插件,是有顺序的。

// 把插件按照配置order进行排序
        final List<SoulPlugin> soulPlugins = pluginList.stream()
                .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());

各个插件顺序,已被定义在一个PluginEnum枚举类型里:

@RequiredArgsConstructor
@Getter
public enum PluginEnum {

    /**
     * Global plugin enum.
     */
    GLOBAL(1, 0, "global"),

    /**
     * Sign plugin enum.
     */
    SIGN(2, 0, "sign"),

    /**
     * Waf plugin enum.
     */
    WAF(10, 0, "waf"),

    /**
     * Rate limiter plugin enum.
     */
    RATE_LIMITER(20, 0, "rate_limiter"),

    /**
     * Context path mapping plugin enum.
     */
    CONTEXTPATH_MAPPING(25, 0, "context_path"),

    /**
     * Rewrite plugin enum.
     */
    REWRITE(30, 0, "rewrite"),

    /**
     * Redirect plugin enum.
     */
    REDIRECT(40, 0, "redirect"),


    /**
     * Hystrix plugin enum.
     */
    HYSTRIX(45, 0, "hystrix"),

    /**
     * Sentinel plugin enum.
     */
    SENTINEL(45, 0, "sentinel"),

    /**
     * Resilence4J plugin enum.
     */
    RESILIENCE4J(45, 0, "resilience4j"),

    /**
     * Divide plugin enum.
     */
    DIVIDE(50, 0, "divide"),

    /**
     * springCloud plugin enum.
     */
    SPRING_CLOUD(50, 0, "springCloud"),

    /**
     * webSocket plugin enum.
     */
    WEB_SOCKET(55, 0, "webSocket"),

    /**
     * Dubbo plugin enum.
     */
    DUBBO(60, 0, "dubbo"),

    /**
     * Sofa plugin enum.
     */
    SOFA(60, 0, "sofa"),

    /**
     * Tars plugin enum.
     */
    TARS(60, 0, "tars"),

    /**
     * Monitor plugin enum.
     */
    MONITOR(80, 0, "monitor"),

    /**
     * Response plugin enum.
     */
    RESPONSE(100, 0, "response");

插件请求处理

每一个http请求过来,SoulWebHandler会处理请求:

 @Override
    public Mono<Void> handle(@NonNull final ServerWebExchange exchange) {
        // TODO:指标监控相关
        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)));
    }

接着DefaultSoulPluginChain中开启执行请求:

    private static class DefaultSoulPluginChain implements SoulPluginChain {

        private int index;

        private final List<SoulPlugin> plugins;

        ......
        @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();
            });
        }
    }
}

依次执行插件:

0 = {GlobalPlugin@7938}
1 = {SignPlugin@10092}
2 = {WafPlugin@10093}
3 = {RateLimiterPlugin@10094}
4 = {HystrixPlugin@10095}
5 = {Resilience4JPlugin@10096}
6 = {DividePlugin@7993}
7 = {WebClientPlugin@9890}
8 = {WebSocketPlugin@10097}
9 = {BodyParamPlugin@10098}
10 = {AlibabaDubboPlugin@10099}
11 = {MonitorPlugin@10100}
12 = {DubboResponsePlugin@10101}
13 = {WebClientResponsePlugin@9998}

已被加载的插件关系图如下:
在这里插入图片描述
soul就是通过责任链的模式,执行每个插件的处理:
1)直接继承SoulPlugin接口的,执行各自插件的execute方法,处理插件逻辑
2)继承AbstractSoulPlugin抽象方法的,执行AbstractSoulPluginexecute方法,执行完之后,路由规则匹配没问题,在执行各自的插件的doExecute方法
3)最后通过插件WebClientResponsePlugin,将请求响应返回给用户。
至此,整个插件处理完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值