Soul源码分析–一次请求的旅行之路

  • Post author:
  • Post category:其他


Soul源码分析–一次请求的旅行之路

我们知道soul是一个异步的、高性能的、跨语言的、响应式的API网关,既然做为一个网关,当然是需要对外暴露,接收外部请求,然后转发到后端真实的机器,这一篇就来分析一下,一个请求经过网关会经历什么,最后安全到达目的地。



divide 插件原理

这里先来看看 divide 插件的原理,这里的启动 admin、bootstrap、excample-http 项目的骚操作就不说了。

  1. 首先配置好选择器的规则,这里选择默认的随机访问负载策略,然后访问 http://localhost:9195/http/order/findById?id=11 请求地址

    在这里插入图片描述

  2. 请求走到网关之后,会找到相应的插件,这里找到的是 DividePlugin 这个插件,执行 doExcecute 方法。首先会找到这个插件配置的相应参数,确保配置没有错误,然后找到相应的 LoadBalance 然后根据 LoadBalance 来决定访问哪个后端的真实服务

    在这里插入图片描述

  3. 先看看第一步是怎么确认负载策略是哪一个,这里首先会调用 LoadBalanceUtils.selector 方法,这里会根据负载策略名称来生成相应的负载类,这里生成的 Random 所以会跳到 RandomLoadBalance 这个类中来。

    public static DivideUpstream selector(final List<DivideUpstream> upstreamList, final String algorithm, final String ip) {
        LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm);
        return loadBalance.select(upstreamList, ip);
    }
    

    然后执行 doSelect 方法,这个方法首先会计算出所有的权重,然后看所有权重是否相同,如果相同就用 RANDOM.nextInt(upstreamList.size()) 方法生成一个可访问长度中的一个随机数,说白了就是随便选一个访问。如果权重都设置为0的话,和权重相同的随机是一样的。

    public DivideUpstream doSelect(final List<DivideUpstream> upstreamList, final String ip) {
        int totalWeight = calculateTotalWeight(upstreamList);
        boolean sameWeight = isAllUpStreamSameWeight(upstreamList);
        if (totalWeight > 0 && !sameWeight) {
            return random(totalWeight, upstreamList);
        }
        // If the weights are the same or the weights are 0 then random
        return random(upstreamList);
    }
    

    如果权重不同的话,需要把 RANDOM.nextInt(totalWeight) 方法算出来的随机数再减去每一个的权重,直到 offset 减到小于0,说明当前这一个权重比上一个权重大,直接返回这个权重大的,如果这个随机数没有小于0,说明第一个权重特别大,返回第一个

    在这里插入图片描述

  4. 最后就是根据返回的随机访问地址,拼出 domain + realURL (比如http://192.168.3.4:8188/order/findById?id=11) 看到这个地址是不是很熟悉,没错,之前在浏览器中访问的地址是网关的地址,这里拼出来的是真实的地址,拿着真实的地址去请求后端服务器



springcloud插件原理

  1. 在 soul 中默认开启的插件是 divide ,其它的手动去开启和关闭,这里先需要把网关中 pom.xml 的依赖给引用上。

    <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>soul-spring-boot-starter-plugin-springcloud</artifactId>
            <version>${project.version}</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
    

    然后在 admin 后台系统管理中的插件管理把 springCloud 插件给打开,然后启动 examples-springcloud 测试项目。

    在这里插入图片描述

    启动完之后,在浏览器输入 http://localhost:9195/springcloud/order/findById?id=11 看返回结果是否成功,不过看这个样子,第一次访问是不成功的,serviceId不存在,下面我们来跟一下到后端去发生了什么。

    在这里插入图片描述

  2. 首先经过的是 SoulWebHandler 类中的 handle 方法,主要是得到默认插件的列表,然后执行对应插件的方法,重点是 execute 方法。

    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 方法中主要做了下列几件事,先判断启用的哪个插件,这里启用的是 springCloud 插件,然后去查询 selectors 选择器,然后找到对应访问的选择器数据,判断 rules 对应上匹配的规则,最后去执行插件相应名称的 doExecute 方法。

    在这里插入图片描述

    输出相应的日志,说明已经匹配上了 springcloud 选择器和选择器中的规则。

    2021-02-06 02:24:10.312  INFO 97277 --- [-work-threads-4] o.d.soul.plugin.base.AbstractSoulPlugin  : springCloud selector success match , selector name :/springcloud
    2021-02-06 02:31:33.706  INFO 97277 --- [-work-threads-4] o.d.soul.plugin.base.AbstractSoulPlugin  : springCloud rule success match , rule name :/springcloud/order/findById
    
  3. 然后执行 doExecute 方法,和 divide 插件套路差不多,判断关键的配置是否正确,然后这里的 loadBalancer 用的是 springcloud 里的的方法,后面就是拼真实的地址,然后根据真实地址去请求真实的服务器了

    在这里插入图片描述



版权声明:本文为yutanbo123原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。