专栏目录
39. 改造 resilience4j 粘合 WebClient 42.SpringCloudGateway 现有的可供分析的请求日志以及缺陷 5.所有项目的parent与spring-framework-common说明 44.避免链路信息丢失做的设计(2) 13.UnderTow 核心配置 45. 实现公共日志记录 1. 背景 35. 验证线程隔离正确性 7.从Bean到SpringCloud 26.OpenFeign的组件 31. FeignClient 实现断路器以及线程隔离限流的思路 8.理解 NamedContextFactory 37. 实现异步的客户端封装配置管理的意义与设计 23.订制Spring Cloud LoadBalancer 14.UnderTow AccessLog 配置介绍 21.Spring Cloud LoadBalancer简介 10.使用Log4j2以及一些核心配置 30. FeignClient 实现重试 17.Eureka的实例配置 11.Log4j2 监控相关 38. 实现自定义 WebClient 的 NamedContextFactory 15.UnderTow 订制 18.Eureka的客户端核心设计和配置 34.验证重试配置正确性 43.为何 SpringCloudGateway 中会有链路信息丢失 28.OpenFeign的生命周期-进行调用 20. 启动一个 Eureka Server 集群 41. SpringCloudGateway 基本流程讲解(2) 16.Eureka架构和核心概念 44.避免链路信息丢失做的设计(1) 22.Spring Cloud LoadBalancer核心源码 27.OpenFeign的生命周期-创建代理 3.Eureka Server 与 API 网关要考虑的问题 32. 改进负载均衡算法 24.测试Spring Cloud LoadBalancer 6.微服务特性相关的依赖说明 19.Eureka的服务端设计与配置 12.UnderTow 简介与内部原理 41. SpringCloudGateway 基本流程讲解(1) 40. spock 单元测试封装的 WebClient(下) 33. 实现重试、断路器以及线程隔离源码 2.微服务框架需要考虑的问题 4.maven依赖回顾以及项目框架结构 36. 验证断路器正确性 40. spock 单元测试封装的 WebClient(上) 29.Spring Cloud OpenFeign 的解析(1) 25.OpenFeign简介与使用 9.如何理解并定制一个Spring Cloud组件

41. SpringCloudGateway 基本流程讲解(2)

unknown
• 阅读 619

41. SpringCloudGateway 基本流程讲解(2)

本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent

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

41. SpringCloudGateway 基本流程讲解(2)

全局 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

41. SpringCloudGateway 基本流程讲解(2)

点赞
收藏
评论区
推荐文章

暂无数据

unknown
unknown
Lv1
男 · rrrr · rrrrrrrr
rrrrr
文章
0
粉丝
17
获赞
0
热门文章

暂无数据