歡迎光臨
每天分享高質量文章

NET Core微服務之路:基於Ocelot的API網關Relay實現–RPC篇

前言

我們都知道,API網關是工作在應用層上網關程式,為何要這樣設計呢,而不是將網關程式直接工作在傳輸層、或者網絡層等等更底層的環境呢?讓我們先來簡單的瞭解一下TCP/IP的五層模型。

(圖片出自http://www.cnblogs.com/qishui/p/5428938.html)

具體的每一層的工作原理想必大家都已經滾瓜爛熟了,筆者也不在重覆的複述這內容。回到上面的問題,為何API網關需要工作在應用層上的問題就變得一目瞭然,物理層面的網關是交給物理設備進行的,例如物理防火牆,而HTTP是網絡通信中已經完全規範化和標準化的應用層協議,隨處可見的通信協議,當然,你把網關集成到FTP上面也可以,增加相應的協議轉換處理即可。

回過頭來,RPC是什麼,是一個協議嗎?不是。確切的說它只是“遠程呼叫”的一個名稱的縮寫,並不是任何規範化的協議,也不是大眾都認知的協議標準,我們更多時候使用時都是創建的自定義化(例如Socket,Netty)的訊息方式進行呼叫,相比http協議,我們省掉了不少http中無用的訊息內容,例如essay-headers訊息頭。本一個簡單的GET請求,傳回一個hello world的請求和響應,元資料就10個位元組左右,但是加上essay-headers訊息頭等等http的標準內容,估計會膨脹到25~30個位元組,下麵是一個常見的http的essay-headers訊息頭。

因此很多系統內部呼叫仍然採用自定義化的RPC呼叫樣式進行通信,畢竟速度和性能是內網的關鍵指標之一,而標準化和語意無關性在外網中舉足輕重。所以,為何API網關無法工作在RPC上,因為它沒有一個像HTTP/HTTPS那樣的通用標準,需要我們將標準化的協議轉為為自定義協議的處理,通常稱為Relay,如圖所示。

上一篇中,我們已經簡單的介紹了Ocelot在Http中的網關實現,無需任何修改,全都可以在配置檔案中完成,相當方便。但是,我們需要實現自定義的RPC協議時,應該怎麼辦呢?

這裡,感謝張隊在上一篇中提供建議和思路https://www.cnblogs.com/SteveLee/p/Ocelot_Api_http_and_https.html#4171964,可以通過增加(或擴展)OcelotMiddleware來處理下游協議的轉換。

(圖片出自https://www.cnblogs.com/shanyou/p/7787183.html)

Ocelot下游中間件擴展

我們知道,在Ocelot網關中,所有操作均通過中間件來進行過濾和處理,而多個中間件之間的相互迭代通信便形成了Ocelot的通信管道,原始碼中使用OcelotPipelineConfiguration來擴展和配置更多的Ocelot中間件,見原始碼所示:

在原始碼中,我們可以看到,所有的中間件對應操作物件的均是DownstreamContext下游背景關係物件。而MapWhenOcelotPipeline正好可以滿足我們擴展中間件的需求,它提供List>>委托以供我們配置多個下游處理中間件並映射到Ocelot管道構建器中。我們查看DownstreamContext的原始碼,可以看到,構建下游背景關係的時候,預設就傳遞了HttpContext物件,而通過DownstreamRequest和DownstreamResponse完成對下游的請求和響應接收。

這樣,我們便可以通過對OcelotPipelineConfiguration的擴展來添加自定義中間件,我們把它擴展名稱定義為OcelotPipelineConfigurationExtensions吧。

當有了DownstreamContext的擴展定義,而且在下游配置中,我們需要指定的配置協議是tcp,那麼我們便可以開始實現這個擴展的中間件了,我們把中間件的名稱定義為RelayRequesterMiddleware

上面加粗的代碼便是下游攔截的主要處理地方,在這裡我們便可以使用http轉rpc的協議轉換處理。當然,在Ocelot的使用配置中,我們需要對該Middleware中間件進行添加。

app.UseOcelot(pipelineConfiguration => pipelineConfiguration.AddRpcMiddleware()).Wait();

以上便完成了對Ocelot中DownstreamContext的擴展,

總結下來,當我們需要擴展下游協議時,我們需要手動配置OcelotPipelineConfiguration並添加到IOcelotPipelineBuilder中,然後通過擴展IOcelotPipelineBuilder實現下游中間件的自定義處理。

手動協議轉換

其實到上面這一小節,相信很多朋友都可以實現自定義的下游攔截和處理了,而本節的介紹,只是針對在Doteasy.RPC中如何進行協議轉換的一個參考。

我們先看一組http中的URL:http://127.0.0.1:8080/api/values,然後再看看tcp中的URL:tcp://127.0.0.1:8080/api/values。有什麼區別嗎?沒有任何區別,唯一的區別是scheme從http轉為tcp。而在rpc過程呼叫中,一般我們是沒有“絕對路徑+謂詞”的方式去識別服務節點的,一般都是tcp://127.0.0.1:8080,而具體指定的服務節點交給註冊中心去完成,也就是通常所說的服務發現。

由於Doteasy.RPC內部並未實現如“://:@:/……”這樣標準化的統一定位,所以筆者的做法是將RPC的客戶端集成到Ocelot宿主中,讓它替代DownstreamConext下游的請求和響應,通過擴展反射的方式實現所有代理的生成,並根據謂詞和引數進行方法的呼叫,這樣,代碼就不需要做太多的修改。

首先需要明白這樣做的一個目的

  1. 在Doteasy.RPC單次呼叫中,為了減少眾多接口生成代理所帶來的耗時,每次呼叫前都會檢查相關接口的動態代理是否已經生成,確保每次只生成一個片段的代理。然而,作為一個網關中的中繼器,這樣一次生成一個代碼片段顯得非常無力,需要將所有的接口全部生成代理,以方便在Relay中查找和呼叫。
  2. 再看一個RESTful風格中的URL:http://127.0.0.1:8080/api/1/Sync,一般我們將謂詞放置最後,而引數一般放置在謂詞的前面,在手動轉換RPC的過程中,就可以利用謂詞來假設我們需要呼叫的RPC服務名稱(但實際不一定就是Sync)。
  3. 基於Doteasy.RPC中的服務容器,可以很方便的實現引數型別轉換以及後期的Headers處理。

筆者的轉換方式是將謂詞作為服務名稱和引數值進行呼叫,雖然這種方式目前來看十分拙劣,但為自定義轉換提供了一組思路,還可以不斷的優化和調整,目前缺點如下:

  1. 當http中多個引數時,無法進行協議轉換,因為不知道代理標的方法的引數集合是多少,只有全域性假設一對一的引數標的。
  2. RPC客戶端在網關中集成了大量的代理生成,無法實現動態更新,例如原來手動替換DLL,接口自動更新動態代理。
  3. 每一次呼叫都需要從大量的代理中查找指定(或模糊匹配)的方法名稱,如果存在1KW+的接口名稱,這個查找將是一個非常嚴重的瓶頸。

總結

世上沒有100%完美的事物,所以才有各種各樣的手段,這裡筆者在Doteasy.RPC和Ocelot的基礎上做了一個簡單下游協議轉換,有興趣的朋友可以自行實現自己想要的協議轉換。再次感謝張隊提供的Ocelot手動轉RPC思路。

 

原文地址:

https://www.cnblogs.com/SteveLee/p/Ocelot_Api_http_route_RPC.html

赞(0)

分享創造快樂