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

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

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

SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本流程讲解(2)的更多相关文章

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

    本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 接下来,将进入我们升级之路的又一大模块,即网关模块.网关模块我们废弃了已经进入维护状态的 ...

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

    本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 我们继续分析上一节提到的 WebHandler.加入 Spring Cloud Sleut ...

  3. SpringCloud升级之路2020.0.x版-42.SpringCloudGateway 现有的可供分析的请求日志以及缺陷

    本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 网关由于是所有外部用户请求的入口,记录这些请求中我们需要的元素,对于线上监控以及业务问题定 ...

  4. SpringCloud升级之路2020.0.x版-1.背景

    本系列为之前系列的整理重启版,随着项目的发展以及项目中的使用,之前系列里面很多东西发生了变化,并且还有一些东西之前系列并没有提到,所以重启这个系列重新整理下,欢迎各位留言交流,谢谢!~ Spring ...

  5. SpringCloud升级之路2020.0.x版-6.微服务特性相关的依赖说明

    本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford spring-cl ...

  6. SpringCloud升级之路2020.0.x版-10.使用Log4j2以及一些核心配置

    本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford 我们使用 Log4 ...

  7. SpringCloud升级之路2020.0.x版-43.为何 SpringCloudGateway 中会有链路信息丢失

    本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 在开始编写我们自己的日志 Filter 之前,还有一个问题我想在这里和大家分享,即在 Sp ...

  8. SpringCloud升级之路2020.0.x版-29.Spring Cloud OpenFeign 的解析(1)

    本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 在使用云原生的很多微服务中,比较小规模的可能直接依靠云服务中的负载均衡器进行内部域名与服务 ...

  9. SpringCloud升级之路2020.0.x版-34.验证重试配置正确性(1)

    本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 在前面一节,我们利用 resilience4j 粘合了 OpenFeign 实现了断路器. ...

随机推荐

  1. 洛谷3203 弹飞绵羊(LCT)

    据说这个题当年的正解是分块qwq 根据题目所说,对于题目中的弹力系数,就相当于一条边,那么对于"跳出去"这个限制,我们可以选择建造一个新点\(n+1\)表示结束,那么每次,求一个点 ...

  2. vj-2021.6.5-补题

    B - B 内容: One day, as Sherlock Holmes was tracking down one very important criminal, he found a wond ...

  3. Markdown数学公式

    1. 显示位置与大小 正文(inline)中的LaTeX公式用$...$定义, 显示在当前行内. $\sum_{i=0}^N\int_{a}^{b}g(t,i)\text{d}t$ \(\sum_{i ...

  4. Markdown Syntax Images

    Markdown Syntax Images Admittedly, it's fairly difficult to devise a "natural" syntax for ...

  5. Pytorch——张量 Tensors

    张量 Tensors 1.torch.is_tensor torch.is_tensor(obj) 用法:判断是否为张量,如果是 pytorch 张量,则返回 True. 参数:obj (Object ...

  6. 初识HTML01

    什么是页面? 页面是基于浏览器的应用程序 页面是数据展示的载体,由浏览器和服务器共同执行产物. 浏览器的功能 向服务器发送用户请求指令 接收并解析数据展示给用户 服务器的功能 存储页面资源 处理并响应 ...

  7. 【Java虚拟机5】Java内存模型(硬件层面的并发优化基础知识--指令乱序问题)

    前言 其实之前大家都了解过volatile,它的第一个作用是保证内存可见,第二个作用是禁止指令重排序.今天系统学习下为什么CPU会指令重排. 存储器的层次结构图 1.CPU乱序执行指令的根源 CPU读 ...

  8. 关于takin-data,你想知道的都在这里(二)trace日志篇

    相信大家在使用takin的过程中都见到过压测过程中实时展示的请求流量明细和请求详情了吧,像这样: 还有这样: 这样的请求流量明细和调用链详情是怎么实现的呢,今天就带大家探究下. 在前面的启动命令篇(h ...

  9. 异常大讨论-抛出异常还是返回false

    iteye精华帖之异常大讨论 原帖链接http://www.iteye.com/topic/2038 Robbin的观点 观点1:Exception实际上代表了一个UseCase中的异常流的处理. 绝 ...

  10. 微信小程序的发布流程

    一.背景 在中大型的公司里,人员的分工非常仔细,一般会有不同岗位角色的员工同时参与同一个小程序项目.为此,小程序平台设计了不同的权限管理使得项目管理者可以更加高效管理整个团队的协同工作 以往我们在开发 ...