前言

经过前面的学习,我们已经知道 soul 网关会接收 http 请求,然后交给 divide 插件去处理。

今天我们就来看看 Divide 插件处理的完整过程。

正文

我们先从 Divide 插件的参数开始,主要的参数就 4 个。

  1. public class DividePlugin extends AbstractSoulPlugin {
  2. @Override
  3. protected Mono<Void> doExecute(final ServerWebExchange exchange,
  4. final SoulPluginChain chain,
  5. final SelectorData selector,
  6. final RuleData rule) {
  7. // 省略代码,先看参数
  8. }
  9. }
  • exchange

这个参数是 ServerWebExchange 的实例,里面主要是 http 请求的 ServerHttpRequest 和 ServerHttpResponse,以及当前 exchange 携带的属性。

  • chain

这是一系列的插件,包括了 Divide 插件、Dubbo 插件等等。这些插件使用了责任链的模式,从头到尾去匹配请求,然后针对性的处理。

  • selector

这个在之前的文章里也提到过很多次了,「选择器」主要是为了匹配请求的 url 而提供的前缀。另外,还会有对应的上游服务器的 IP 和端口等信息。

  • rule

这个参数同样也在前面的文章多次提到,「规则」的主要作用是存储了请求的完整 url,并且可以针对这个 url 接口设置负载均衡策略。

弄清楚了参数的意义,我们继续看程序是怎么去处理的。

结合之前的文章,我们知道这个 Divide 插件就是从「选择器」里面拿到上游服务器的列表,然后借助「规则」里面的负载均衡策略去选出一个合适的服务器,在此基础上拼接上「规则」的 url,最后交给 WebClientPlugin 插件去发起 http 请求并且返回给调用方。

我们先看是怎么拿到上游服务器列表数据的。

  1. public final class UpstreamCacheManager {
  2. private static final Map<String, List<DivideUpstream>> UPSTREAM_MAP_TEMP = Maps.newConcurrentMap();
  3. public List<DivideUpstream> findUpstreamListBySelectorId(final String selectorId) {
  4. return UPSTREAM_MAP_TEMP.get(selectorId);
  5. }
  6. }

从代码里可以看到这个过程就是简单的从内存里面根据「选择器」ID 去 Map 里取值,这个 Map 会在 soul 网关启动完成之后通过数据同步模块赋值,在之前的数据同步相关的文章已经介绍过了。

拿到了上游的服务器之后,就是根据「规则」里面的负载均衡策略去选择服务器了,目前提供了 3 种负载均衡策略。

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

image.png
hash 就是使用 hash 函数计算请求的 ip 地址;random 就是根据上游服务器列表计算出一个随机数去选择。

最后的 roundRobin 是轮训调度的方式,就比如第一次是列表里的第一个服务器,第二次就是列表的第二个服务器,挨个轮下去。

最后就是 url 的拼接过程,然后存粗到 exchange 里面,让之后的插件去执行请求并返回给调用端。

  1. // 获取到服务器地址,存储到当前的 exchange
  2. String domain = buildDomain(divideUpstream);
  3. String realURL = buildRealURL(domain, soulContext, exchange);
  4. exchange.getAttributes().put(Constants.HTTP_URL, realURL);
  5. // set the http timeout
  6. exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout());
  7. exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry());
  8. return chain.execute(exchange);

值得一提的是,这里面还设置了接口的超时时间和重试次数。

总结

Divide 插件是对 http 请求的正向代理,利用了责任链的模式,专门去处理 http 的请求。

主要的内容就是对「选择器」和「规则」的匹配和处理,目的是构建出一个完整的符合预期规则的 http 请求 url,然后交给下一个插件去处理 http 的返回值。