SpringCloud升級(jí)之路2020.0.x版-41.

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

我們繼續(xù)分析上一節(jié)提到的 ??WebHandler??,經(jīng)過(guò)將請(qǐng)求封裝成 ServerWebExchange 的 HttpWebHandlerAdapter 之后,請(qǐng)求會(huì)經(jīng)過(guò) ExceptionHandlingWebHandler

SpringCloud升級(jí)之路2020.0.x版-41.

全局 Web 處理異常處理器的接入點(diǎn) - ExceptionHandlingWebHandler

之前有網(wǎng)友私信問(wèn)過(guò)筆者,如何給 Spring Cloud Gateway 加全局異常處理器,其實(shí)和給基于 Spring-Flux 的異步 Web 服務(wù)加是一樣的,都是通過(guò)實(shí)現(xiàn)并注冊(cè)一個(gè) ??WebExceptionHandler?? Bean

??WebExceptionHandler.java??

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

這些 Bean,就是在 ExceptionHandlingWebHandler 被加入到整個(gè)請(qǐng)求處理鏈路中的:

??ExceptionHandlingWebHandler.java??

@Override
public Mono handle(ServerWebExchange exchange) {
Mono completion;
try {
//這里其實(shí)就是組裝后面的鏈路,即調(diào)用后面的 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;
}

從源碼可以看出,這里將每個(gè) ??WebExceptionHandler?? 作為 Mono 的異常處理 ??onErrorResume?? 加入了鏈路。??onErrorResume?? 的意思是如果鏈路前面發(fā)生異常,則在這里捕獲住異常同時(shí)調(diào)用 ??handler.handle(exchange, ex)?? 進(jìn)行處理,如果使用阻塞代碼理解,就相當(dāng)于:

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

這里我們看到有多個(gè) ??WebExceptionHandler??,都會(huì)在鏈路后面追加 ??onErrorResume??,其實(shí)就相當(dāng)于:

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

轉(zhuǎn)換成阻塞代碼理解,其實(shí)就是:

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)
//如果還有就繼續(xù)疊加
}
}
}

當(dāng) WebExceptionHandler 可以處理這個(gè)異常的時(shí)候,他的 ??handle?? 方法會(huì)返回一個(gè)真正的響應(yīng),否則會(huì)返回異常,例如:

public class WebExceptionHandler1 implements WebExceptionHandler {
@Override
public Mono handle(ServerWebExchange exchange, Throwable ex) {
//如果是 ResponseStatusException 則使用異常里面的響應(yīng)碼和 HTTP 頭填充響應(yīng)的響應(yīng)碼和 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)));
//返回響應(yīng)完成
return response.setComplete();
}
//拋出異常,繼續(xù)鏈路異常處理
return Mono.error(ex);
}
}

轉(zhuǎn)換成同步代碼去理解其實(shí)就是:

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)));
//返回響應(yīng)完成
return response.setComplete();
}
//拋出異常,繼續(xù)鏈路異常處理
throw ex;

如果大家想封裝自己統(tǒng)一的錯(cuò)誤響應(yīng),可以通過(guò)實(shí)現(xiàn)這個(gè)接口進(jìn)行實(shí)現(xiàn)。

DefaultWebFilterChain 的鏈路起點(diǎn) - FilteringWebHandler

接下來(lái)進(jìn)入 FilteringWebHandler,注意是 ??org.springframework.web.server.handler.FilteringWebHandler?? 而不是 Spring Cloud Gateway 的 ??org.springframework.cloud.gateway.handler.FilteringWebHandler??。在這里,會(huì)將上下文中載入的 WebFilter 拼接成 ??DefaultWebFilterChain??,然后調(diào)用其 filter 方法:

private final DefaultWebFilterChain chain;

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

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

Spring Cloud Gateway 的 FilteringWebHandler, 它是 Spring Cloud Gateway 的處理請(qǐng)求業(yè)務(wù)的起點(diǎn)。在這里我們即將進(jìn)入整個(gè) Spring Cloud Gateway 的 Filter 鏈路,包括每個(gè)路徑自己的 ??GatewayFilter?? 以及全局的 ??GlobalGatewayFilter??,都是在這里開(kāi)始被處理組裝成完整調(diào)用鏈路的。我們后面還會(huì)提到

由于我們的項(xiàng)目依賴(lài)中包含了 Spring Cloud Sleuth 以及 Prometheus 的依賴(lài),所以我們這里的 WebFilter 會(huì)包括三個(gè):

  • ??org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter??:添加 Prometheus 相關(guān)依賴(lài)之后,會(huì)有這個(gè) MetricsWebFilter,用于記錄請(qǐng)求處理耗時(shí),采集相關(guān)指標(biāo)。
  • ??org.springframework.cloud.sleuth.instrument.web.TraceWebFilter??:添加 Spring Cloud Sleuth 相關(guān)依賴(lài)之后,會(huì)有這個(gè) TraceWebFilter。
  • ??org.springframework.cloud.gateway.handler.predicate.WeightCalculatorWebFilter??:Spring Cloud Gateway 路由權(quán)重相關(guān)配置功能相關(guān)實(shí)現(xiàn)類(lèi),這個(gè)我們這里不關(guān)心。

其具體流程,我們?cè)谙乱还?jié)中繼續(xù)詳細(xì)分析。

微信搜索“我的編程喵”關(guān)注公眾號(hào),每日一刷,輕松提升技術(shù),斬獲各種offer

SpringCloud升級(jí)之路2020.0.x版-41.