javascript
Spring Cloud Gateway一次请求调用源码解析
簡介:?最近通過深入學習Spring Cloud Gateway發現這個框架的架構設計非常簡單、有效,很多組件的設計都非常值得學習,本文就Spring Cloud Gateway做一個簡單的介紹,以及針對一次請求Spring Cloud Gateway的處理流程做一個較為詳細的分析。
作者 | 尋箏
來源 | 阿里技術公眾號
一 前言
最近通過深入學習Spring Cloud Gateway發現這個框架的架構設計非常簡單、有效,很多組件的設計都非常值得學習,本文就Spring Cloud Gateway做一個簡單的介紹,以及針對一次請求Spring Cloud Gateway的處理流程做一個較為詳細的分析。
二 簡介
Spring Cloud Gateway 即Spring官方推出的一款API網關,該框架包含了Spring5、SpringBoot2、Project Reactor,其中底層通信框架用的netty。Spring Cloud Gateway在推出之初的時候,Netflix公司已經推出了類似功能的API網關框架ZUUL,但ZUUL有一個缺點是通信方式是阻塞的,雖然后來升級到了非阻塞式的ZUUL2,但是由于Spring Cloud Gateway已經推出一段時間,同時自身也面臨資料少、維護性較差的因素沒有被廣泛應用。
1 關鍵術語
在使用Spring Cloud Gateway的時候需要理解三個模塊,即
Route:
即一套路由規則,是集URI、predicate、filter等屬性的一個元數據類。
Predicate:
這是Java8函數式編程的一個方法,這里可以看做是滿足什么條件的時候,route規則進行生效。
Filter:
filter可以認為是Spring Cloud Gateway最核心的模塊,熔斷、安全、邏輯執行、網絡調用都是filter來完成的,其中又細分為gateway filter和global filter,區別在于是具體一個route規則生效還是所有route規則都生效。
可以先上一段代碼來看看:
@RequestMapping("/paramTest")public Object paramTest(@RequestParam Map<String,Object> param) {return param.get("name");}@Beanpublic RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes().route("path_route", r ->r.path("/get").filters(f -> f.addRequestParameter("name", "value")).uri("forward:///paramTest")).build();}- route方法代表的就是一個路由規則;
- path方法代表的就是一個predicate,背后的現實是PathRoutePredicateFactory,在這段代碼的含義即當路徑包含/get的時候,當前規則生效。
- filters方法的意思即給當前路由規則添加一個增加請求參數的filter,每次請求都對參數里添加 name:value 的鍵值對;
- uri 方法的含義即最終路由到哪里去,這里的forward前綴會將請求交給spring mvc的DispatcherHandler進行路由,進行本機的邏輯調用,除了forward以外還可以使用http、https前綴進行http調用,lb前綴可以在配置注冊中心后進行rpc調用。
上圖是Spring Cloud Gateway官方文檔給出的一個工作原理圖,Spring Cloud Gateway 接收到請求后進行路由規則的匹配,然后交給web handler 進行處理,web handler 會執行一系列的filter邏輯。
三 流程分析
1 接受請求
Spring Cloud Gateway的底層框架是netty,接受請求的關鍵類是ReactorHttpHandlerAdapter,做的事情很簡單,就是將netty的請求、響應轉為http的請求、響應并交給一個http handler執行后面的邏輯,下圖為該類的源碼僅保留核心邏輯。
@Overridepublic Mono< Void> apply(HttpServerRequest request, HttpServerResponse response) {NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(response.alloc());ServerHttpRequest adaptedRequest;ServerHttpResponse adaptedResponse;//轉換請求try {adaptedRequest = new ReactorServerHttpRequest(request, bufferFactory);adaptedResponse = new ReactorServerHttpResponse(response, bufferFactory);}catch (URISyntaxException ex) {if (logger.isWarnEnabled()) {...}...return this.httpHandler.handle(adaptedRequest, adaptedResponse).doOnError(ex -> logger.warn("Handling completed with error: " + ex.getMessage())).doOnSuccess(aVoid -> logger.debug("Handling completed with success"));}2 WEB過濾器鏈
http handler做的事情第一是將request 和 response轉為一個exchange,這個exchange非常核心,是各個filter之間參數流轉的載體,該類包含request、response、attributes(擴展字段),接著做的事情就是web filter鏈的執行,其中的邏輯主要是監控。
其中WebfilterChainParoxy 又會引出新的一條filter鏈,主要是安全、日志、認證相關的邏輯,由此可見Spring Cloud Gateway的過濾器設計是層層嵌套,擴展性很強。
3 尋找路由規則
核心類是RoutePredicateHandlerMapping,邏輯也非常簡單,就是把所有的route規則的predicate遍歷一遍看哪個predicate能夠命中,核心代碼是:
return this.routeLocator.getRoutes().filter(route -> {...return route.getPredicate().test(exchange);})因為我這里用的是path進行過濾,所以背后的邏輯是PathRoutePredicateFactory來完成的,除了PathRoutePredicateFactory還有很多predicate規則。
這些路由規則都能從官方文檔上找到影子。
4 核心過濾器鏈執行
找到路由規則后下一步就是執行了,這里的核心類是FilteringWebHandler,其中的源碼為:
做的事情很簡單:
因為我的配置里包含了一個添加請求參數的邏輯,所以紅線箭頭處就是我配置的gateway filter名為 AddRequestParameterGatewayFilterFactory,其余全是Gloabl Filter,這些過濾器的功能主要是url解析,請求轉發,響應回寫等邏輯,因為我們這里用的是forward schema,所以請求轉發會由ForwardRoutingFilter進行執行。
5 請求轉發
ForwardRoutingFilter做的事情也很簡單,直接復用了spring mvc的能力,將請求提交給dispatcherHandler進行處理,dispatcherHandler會根據path前綴找到需要目標處理器執行邏輯。
@Override public Mono< Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);String scheme = requestUrl.getScheme();if (isAlreadyRouted(exchange) || !"forward".equals(scheme)) {return chain.filter(exchange);}setAlreadyRouted(exchange);//TODO: translate url?if (log.isTraceEnabled()) {log.trace("Forwarding to URI: "+requestUrl);}return this.dispatcherHandler.handle(exchange); }6 響應回寫
響應回寫的核心類是NettyWriteResponseFilter,但是大家可以注意到執行器鏈中NettyWriteResponseFilter的排序是在最前面的,按道理這種響應處理的類應該是在靠后才對,這里的設計比較巧妙。大家可以看到chain.filter(exchange).then(),意思就是執行到我的時候直接跳過下一個,等后面的過濾器都執行完后才執行這段邏輯,這種行為控制的方法值得學習。
@Override public Mono< Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// NOTICE: nothing in "pre" filter stage as CLIENT_RESPONSE_ATTR is not added// until the WebHandler is runreturn chain.filter(exchange).then(Mono.defer(() -> {HttpClientResponse clientResponse = exchange.getAttribute(CLIENT_RESPONSE_ATTR);if (clientResponse == null) {return Mono.empty();}log.trace("NettyWriteResponseFilter start");ServerHttpResponse response = exchange.getResponse();NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory();//TODO: what if it's not nettyfinal Flux< NettyDataBuffer> body = clientResponse.receive().retain() //TODO: needed?.map(factory::wrap);MediaType contentType = response.getHeaders().getContentType();return (isStreamingMediaType(contentType) ?response.writeAndFlushWith(body.map(Flux::just)) : response.writeWith(body));})); }四 總結
整體讀完Spring Cloud Gateway請求流程代碼后,有幾點感受:
原文鏈接
本文為阿里云原創內容,未經允許不得轉載。?
總結
以上是生活随笔為你收集整理的Spring Cloud Gateway一次请求调用源码解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: “CV_RGB2BGR”: 未声明的标识
- 下一篇: sealer背后实现整个集群一键交付的奥