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

語言: CN / TW / HK

這是我參與11月更文挑戰的第25天,活動詳情檢視:2021最後一次更文挑戰

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

我們繼續分析上一節提到的 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 handle(ServerWebExchange exchange) { Mono 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 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 的處理請求業務的起點。在這裡我們即將進入整個 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

「其他文章」