前言
在上一篇文章《Soul网关源码学习(11)- Http代理的负载均衡:DividePlugin 和 SpringCloudPlugin》 中,我们分析了 Soul 对于负载均的实现,下一步就是发起对目标服务的请求了,那么本篇文章,我们就来分析 Http 代理转发的最后两站 WebClientPlugin 和 WebClientResponsePlugin。
WebClientPlugin
WebClientPlugin 是一个 Http 客户端,用来向目标服务器发起请求,其源码的逻辑也很简单,这里就和大家简单分析一下。WebClientPlugin 并没有继承模板类 AbstractSoulPlugin,而是自己实现了接口方法 execute:
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
...
String urlPath = exchange.getAttribute(Constants.HTTP_URL);
...
HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue());
//构建请求
WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath);
//发起请求
return handleRequestBody(requestBodySpec, exchange, timeout, retryTimes, chain);
}
从上面的代码可看到,真正发起请求的方法是 handleRequestBody ,我们再来看一下这个方法:
private Mono<Void> handleRequestBody(...) {
return requestBodySpec
...
.contentType(buildMediaType(exchange))
.body(BodyInserters.fromDataBuffers(exchange.getRequest().getBody()))
.exchange()
...
.flatMap(e -> doNext(e, exchange, chain));
}
private Mono<Void> doNext(final ClientResponse res, final ServerWebExchange exchange, final SoulPluginChain chain) {
...
exchange.getAttributes().put(Constants.CLIENT_RESPONSE_ATTR, res);
return chain.execute(exchange);
}
上面的代码也非常的简单,exchage() 发起请求,doNext() 处理返回,然后把响应存储到请求上下文 ServerWebExchange 中,然后进入下一个插件的处理。
WebClientResponsePlugin
WebClientResponsePlugin 顾名思义,就是处理上一步的请求响应的,和 WebClientPlugin 一样,其也是直接实现了 execute 方法:
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
return chain.execute(exchange).then(Mono.defer(() -> {
ServerHttpResponse response = exchange.getResponse();
ClientResponse clientResponse = exchange.getAttribute(Constants.CLIENT_RESPONSE_ATTR);
//下面省略一些错误处理,其会使用 WebFluxResultUtils.result(exchange, error) 把错误信息写回给客户端。
...
//下面是正常响应
//把结果写回给上游的客户端
response.setStatusCode(clientResponse.statusCode());
response.getCookies().putAll(clientResponse.cookies());
response.getHeaders().putAll(clientResponse.headers().asHttpHeaders());
return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers()));
}));
}
上面的代码可以注意一下 then 方法,它会在 chain.execute 之后被调用,也就是说在真正写入响应给上游客户端之前,插件链还有可能会执行剩余的其他一些非功能性插件,比如 MonitorPlugin(假如你打开的话),只有这些插件都执行完毕后才会进入 then 方法。
总结
到这里,我们总体回顾一下前面所学的内容。第6、7章,我们梳理了代理转发流程的概览,简单来说就是一个请求从进入 soul 网关到离开 soul 网关,中间会经过哪些关键的类。第 8 ~ 12章,我们以 Http 请求为示例,跟踪并详细分析了其经过的每一个插件的底层原理和实现,让我们对整个代理转发流程有了更加深层次的理解。在进入下一部分内容之前,我们最后再通过一幅流程图来总结一下 Http 代理转发的流程: