我們繼續分析上一節提到的 ??WebHandler?
?,經過將請求封裝成 ServerWebExchange 的 HttpWebHandlerAdapter 之后,請求會經過 ExceptionHandlingWebHandler
全局 Web 處理異常處理器的接入點 - ExceptionHandlingWebHandler
之前有網友私信問過筆者,如何給 Spring Cloud Gateway 加全局異常處理器,其實和給基于 Spring-Flux 的異步 Web 服務加是一樣的,都是通過實現并注冊一個 ??WebExceptionHandler?
? Bean:
public interface WebExceptionHandler {
Monohandle(ServerWebExchange exchange, Throwable ex);
}
這些 Bean,就是在 ExceptionHandlingWebHandler 被加入到整個請求處理鏈路中的:
??ExceptionHandlingWebHandler.java??
@Override
public Monohandle(ServerWebExchange exchange) {
Monocompletion;
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 Monohandle(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, Listfilters) {
super(handler);
this.chain = new DefaultWebFilterChain(handler, filters);
}
@Override
public Monohandle(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: