8

SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本流程讲解(2)

 2 years ago
source link: https://segmentfault.com/a/1190000041019576
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本系列代码地址:https://github.com/JoJoTec/sp...

我们继续分析上一节提到的 WebHandler,经过将请求封装成 ServerWebExchange 的 HttpWebHandlerAdapter 之后,请求会经过 ExceptionHandlingWebHandler

image

全局 Web 处理异常处理器的接入点 - ExceptionHandlingWebHandler

之前有网友私信问过笔者,如何给 Spring Cloud Gateway 加全局异常处理器,其实和给基于 Spring-Flux 的异步 Web 服务加是一样的,都是通过实现并注册一个 WebExceptionHandler Bean

WebExceptionHandler.java

public interface WebExceptionHandler {
    Mono<Void> handle(ServerWebExchange exchange, Throwable ex);
}

这些 Bean,就是在 ExceptionHandlingWebHandler 被加入到整个请求处理链路中的:

ExceptionHandlingWebHandler.java

@Override
public Mono<Void> handle(ServerWebExchange exchange) {
    Mono<Void> completion;
    try {
        //这里其实就是组装后面的链路,即调用后面的 FilteringWebHandler 的 handle
        completion = super.handle(exchange);
    }
    catch (Throwable ex) {
        completion = Mono.error(ex);
    }

    for (WebExceptionHandler handler : this.exceptionHandlers) {
        completion = completion.onErrorResume(ex -> handler.handle(exchange, ex));
    }
    return completion;
}

从源码可以看出,这里将每个 WebExceptionHandler 作为 Mono 的异常处理 onErrorResume 加入了链路。onErrorResume 的意思是如果链路前面发生异常,则在这里捕获住异常同时调用 handler.handle(exchange, ex) 进行处理,如果使用阻塞代码理解,就相当于:

try {
    //前面的链路
} catch(Throwable ex) {
    return handler.handle(exchange, ex)
}

这里我们看到有多个 WebExceptionHandler,都会在链路后面追加 onErrorResume,其实就相当于:

completion.onErrorResume(ex -> webExceptionHandler1.handle(exchange, ex)).onErrorResume(ex -> webExceptionHandler2.handle(exchange, ex)).onErrorResume(ex -> webExceptionHandler3.handle(exchange, ex))...

转换成阻塞代码理解,其实就是:

try {
    completion
} catch(Throwable e1) {
    try {
        return webExceptionHandler1.handle(exchange, e1)
    } catch(Throwable e2) {
        try {
            return webExceptionHandler2.handle(exchange, ex)
        } catch(Throwable e2) {
            return webExceptionHandler3.handle(exchange, ex)
            //如果还有就继续叠加
        }
    }
}

当 WebExceptionHandler 可以处理这个异常的时候,他的 handle 方法会返回一个真正的响应,否则会返回异常,例如:

public class WebExceptionHandler1 implements WebExceptionHandler {
    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        //如果是 ResponseStatusException 则使用异常里面的响应码和 HTTP 头填充响应的响应码和 HTTP 头
        if (ex instanceof ResponseStatusException) {
            ServerHttpResponse response = exchange.getResponse();
            ResponseStatusException responseStatusException = (ResponseStatusException) ex;
            response.setRawStatusCode(responseStatusException.getRawStatusCode());
            responseStatusException.getResponseHeaders()
                    .forEach((name, values) ->
                            values.forEach(value -> response.getHeaders().add(name, value)));
            //返回响应完成
            return response.setComplete();
        }
        //抛出异常,继续链路异常处理
        return Mono.error(ex);
    }
}

转换成同步代码去理解其实就是:

if (ex instanceof ResponseStatusException) {
    ServerHttpResponse response = exchange.getResponse();
    ResponseStatusException responseStatusException = (ResponseStatusException) ex;
    response.setRawStatusCode(responseStatusException.getRawStatusCode());
    responseStatusException.getResponseHeaders()
            .forEach((name, values) ->
                    values.forEach(value -> response.getHeaders().add(name, value)));
    //返回响应完成
    return response.setComplete();
}
//抛出异常,继续链路异常处理
throw ex;

如果大家想封装自己统一的错误响应,可以通过实现这个接口进行实现。

DefaultWebFilterChain 的链路起点 - FilteringWebHandler

接下来进入 FilteringWebHandler,注意是 org.springframework.web.server.handler.FilteringWebHandler 而不是 Spring Cloud Gateway 的 org.springframework.cloud.gateway.handler.FilteringWebHandler。在这里,会将上下文中载入的 WebFilter 拼接成 DefaultWebFilterChain,然后调用其 filter 方法:

private final DefaultWebFilterChain chain;

public FilteringWebHandler(WebHandler handler, List<WebFilter> filters) {
    super(handler);
    this.chain = new DefaultWebFilterChain(handler, filters);
}

@Override
public Mono<Void> handle(ServerWebExchange exchange) {
    return this.chain.filter(exchange);
}

Spring Cloud Gateway 的 FilteringWebHandler, 它是 Spring Cloud Gateway 的处理请求业务的起点。在这里我们即将进入整个 Spring Cloud Gateway 的 Filter 链路,包括每个路径自己的 GatewayFilter 以及全局的 GlobalGatewayFilter,都是在这里开始被处理组装成完整调用链路的。我们后面还会提到

由于我们的项目依赖中包含了 Spring Cloud Sleuth 以及 Prometheus 的依赖,所以我们这里的 WebFilter 会包括三个:

  • org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter:添加 Prometheus 相关依赖之后,会有这个 MetricsWebFilter,用于记录请求处理耗时,采集相关指标。
  • org.springframework.cloud.sleuth.instrument.web.TraceWebFilter:添加 Spring Cloud Sleuth 相关依赖之后,会有这个 TraceWebFilter。
  • org.springframework.cloud.gateway.handler.predicate.WeightCalculatorWebFilter:Spring Cloud Gateway 路由权重相关配置功能相关实现类,这个我们这里不关心。

其具体流程,我们在下一节中继续详细分析。

微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK