文章目录
前言
在前一篇文章《Soul网关源码学习(8)- 代理转发入口 SoulWebHandler》 中,我们详细分析了网关的入口 SoulWebHandler,并且就代码中使用的几个技术进行了扩展延申学习。如果小伙伴看过前面两篇关于 soul 代理转发流程的介绍文章,都应该知道请求进入 SoulWebHandler 之后,下一站就是天后了!噗!开玩笑的!是 GlobalPlugin !
GlobalPlugin
GlobalPlugin 到底是干什么的?
请求解析!这是我对 GlobalPlugin 功能的一个理解。Soul 对外统一使用 Http 接口,所以单纯的直接转发无法实现对不同协议的代理,比如,你总不能直接把 Http 请求转给 dubbo服务器吧,那样 dubbo 服务根本无法识别,需要 soul 通过客户端的请求和控制台配置的元数据进行匹配和结合才能实现。所以这里总结一下其功能:
- 通过 Http 的请求信息匹配相应的元数据,并且添加到 ServerWebExchange 属性中。元数据是 soul 对具体目标服务接口的描述信息,比如 App Name、MethodName、Path等,像 dubbo 的泛化调用就需要使用到元数据,否则仅仅靠客户端请求信息是无法完成代理的。
- 通过分析 Http 请求信息 和 上面获取的元数据构建 SoulContext 对象,并且存储到 ServerWebExchange(Spring WebFlux 的请求上下文) 的属性中,通过后者在整个 请求流程中传递。
SoulContext 的构建
SoulContext 按照字面理解就是 Soul 请求的上下文,它的层次在 ServerWebExchange 之上,毕竟 SoulContext 就存储在 ServerWebExchange 的属性中。
// GlobalPlugin execute 方法中的代码
exchange.getAttributes().put(Constants.CONTEXT, soulContext);
它主要存储一些与目标服务协议相关的信息,比如 Http 目标服务的 HttpMethod、参数、真实Url,又或者 Dubbo 目标服务的服务名称,接口名称、接口参数等待。
SoulContext 的构建代码:
@Override
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
...
// 如果不是 websocket 请求 通过 SoulContextBuilder 构建
if (StringUtils.isBlank(upgrade) || !"websocket".equals(upgrade)) {
soulContext = builder.build(exchange);
} else {
// websocket
final MultiValueMap<String, String> queryParams = request.getQueryParams();
soulContext = transformMap(queryParams);
}
// 存储到 ServerWebExchange 属性中
exchange.getAttributes().put(Constants.CONTEXT, soulContext);
return chain.execute(exchange);
}
上面代码逻辑很简单,通过 Http header "upgrade " 来判断是否是 webSocket 请求,从而选择不同的构建方法。
我们这里主要看非 webSocket 的构建逻辑:

@Override
public SoulContext build(final ServerWebExchange exchange) {
final ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
//获取元数据,并且存到 ServerWebExchange 属性中
MetaData metaData = MetaDataCache.getInstance().obtain(path);
if (Objects.nonNull(metaData) && metaData.getEnabled()) {
exchange.getAttributes().put(Constants.META_DATA, metaData);
}
//通过请求信息和元数据共同构建 soulContext 对象并且返回
return transform(request, metaData);
}
从上面的代码和注释中,可以看到一个关键单词就是 MetaData。
MetaData 的获取
-
首先,MetaData 是什么?
MetaData 可以在 soul admin 中直接查看,其实就是对每个被代理接口的具体描述(例如:dubbo 泛化调用就需要用到),普通 Http 的 MetaData 是空的,这个可以参考一下官方文档说明。 -
MetaData 来自哪里?
源头来自服务注册中心,网关服务获取的是本地副本!Soul Admin 通过配置的服务注册中心,获取到相应的接口描述信息(比如 dubbo 注册在 zookeeper 的接口信息),然后同步到网关服务端,服务端再缓存在本地 JVM 中。数据的同步可能是推或者拉的模式,这个后面“数据同步”相关文章再分析。
PS:源头来自服务注册中心,目前是我推测的,我还没深入看这部分源码(这部分代码应该在 soul admin 中,目前优先解读 soul 服务端)。但是这些信息来自注册中心可能性最大,毕竟网关代理具体的微服务时,需要配置相应的注册中心。后面我回去看这部分代码,求证了再回来修改这里了。
// 从本地缓存中获取 MetaData MetaData metaData = MetaDataCache.getInstance().obtain(path);
生成 SoulContext 对象
SoulContext 对象是通过 DefaultSoulContextBuilder#transform(ServerHttpRequest,MetaData) 方法构建的,代码比较长但是逻辑很简单,所以这里就不贴了,就和开头说的一样,其属性都是通过解析 request 和 MetaData中获取的,因此都是一些 set 操作。
总结
本篇文章,详细介绍了 GlobalPlugin 的作用,通过解析客户端请求,匹配缓存里的元数据,为后续的流程操作提供必要的信息。GlobalPlugin 是所有协议代理都会经过的插件节点,再往后,流程就可能会因为协议的不同而出现分叉了,这也是后面的文章的主要内容。