具體的每一層的工作原理想必大家都已經(jīng)滾瓜爛熟了,筆者也不在重復的復述這內(nèi)容。回到上面的問題,為何API網(wǎng)關(guān)需要工作在應用層上的問題就變得一目了然,物理層面的網(wǎng)關(guān)是交給物理設(shè)備進行的,例如物理防火墻,而HTTP是網(wǎng)絡(luò)通信中已經(jīng)完全規(guī)范化和標準化的應用層協(xié)議,隨處可見的通信協(xié)議,當然,你把網(wǎng)關(guān)集成到FTP上面也可以,增加相應的協(xié)議轉(zhuǎn)換處理即可。

回過頭來,RPC是什么,是一個協(xié)議嗎?不是。確切的說它只是“遠程調(diào)用”的一個名稱的縮寫,并不是任何規(guī)范化的協(xié)議,也不是大眾都認知的協(xié)議標準,我們更多時候使用時都是創(chuàng)建的自定義化(例如Socket,Netty)的消息方式進行調(diào)用,相比http協(xié)議,我們省掉了不少http中無用的消息內(nèi)容,例如headers消息頭。本一個簡單的GET請求,返回一個hello world的請求和響應,元數(shù)據(jù)就10個字節(jié)左右,但是加上headers消息頭等等http的標準內(nèi)容,估計會膨脹到25~30個字節(jié),下面是一個常見的http的headers消息頭。

因此很多系統(tǒng)內(nèi)部調(diào)用仍然采用自定義化的RPC調(diào)用模式進行通信,畢竟速度和性能是內(nèi)網(wǎng)的關(guān)鍵指標之一,而標準化和語義無關(guān)性在外網(wǎng)中舉足輕重。所以,為何API網(wǎng)關(guān)無法工作在RPC上,因為它沒有一個像HTTP/HTTPS那樣的通用標準,需要我們將標準化的協(xié)議轉(zhuǎn)為為自定義協(xié)議的處理,通常稱為Relay,如圖所示。

之前,我們已經(jīng)簡單的介紹了Ocelot在Http中的網(wǎng)關(guān)實現(xiàn),無需任何修改,全都可以在配置文件中完成,相當方便。可以通過增加(或擴展)OcelotMiddleware來處理下游協(xié)議的轉(zhuǎn)換。

Ocelot下游中間件擴展

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

在源碼中,我們可以看到,所有的中間件對應操作對象的均是DownstreamContext下游上下文對象。而MapWhenOcelotPipeline正好可以滿足我們擴展中間件的需求,它提供List<Func<IOcelotPipelineBuilder, Func<DownstreamContext, bool>>>委托以供我們配置多個下游處理中間件并映射到Ocelot管道構(gòu)建器中。我們查看DownstreamContext的源碼,可以看到,構(gòu)建下游上下文的時候,默認就傳遞了HttpContext對象,而通過DownstreamRequest和DownstreamResponse完成對下游的請求和響應接收。

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

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

上面加粗的代碼便是下游攔截的主要處理地方,在這里我們便可以使用http轉(zhuǎn)rpc的協(xié)議轉(zhuǎn)換處理。當然,在Ocelot的使用配置中,我們需要對該Middleware中間件進行添加。app.UseOcelot(pipelineConfiguration => pipelineConfiguration.AddRpcMiddleware()).Wait();

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

總結(jié)下來,當我們需要擴展下游協(xié)議時,我們需要手動配置OcelotPipelineConfiguration并添加到IOcelotPipelineBuilder中,然后通過擴展IOcelotPipelineBuilder實現(xiàn)下游中間件的自定義處理。

手動協(xié)議轉(zhuǎn)換

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

我們先看一組http中的URL:http://127.0.0.1:8080/api/values,然后再看看tcp中的URL:tcp://127.0.0.1:8080/api/values。有什么區(qū)別嗎?沒有任何區(qū)別,唯一的區(qū)別是scheme從http轉(zhuǎn)為tcp。而在rpc過程調(diào)用中,一般我們是沒有“絕對路徑+謂詞”的方式去識別服務(wù)節(jié)點的,一般都是tcp://127.0.0.1:8080,而具體指定的服務(wù)節(jié)點交給注冊中心去完成,也就是通常所說的服務(wù)發(fā)現(xiàn)。

由于Doteasy.RPC內(nèi)部并未實現(xiàn)如“<scheme>://<username>:<password>@<host>:<port>/<path>……”這樣標準化的統(tǒng)一定位,所以筆者的做法是將RPC的客戶端集成到Ocelot宿主中,讓它替代DownstreamConext下游的請求和響應,通過擴展反射的方式實現(xiàn)所有代理的生成,并根據(jù)謂詞和參數(shù)進行方法的調(diào)用,這樣,代碼就不需要做太多的修改。

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

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

筆者的轉(zhuǎn)換方式是將謂詞作為服務(wù)名稱和參數(shù)值進行調(diào)用,雖然這種方式目前來看十分拙劣,但為自定義轉(zhuǎn)換提供了一組思路,還可以不斷的優(yōu)化和調(diào)整,目前缺點如下:

  1. http中多個參數(shù)時,無法進行協(xié)議轉(zhuǎn)換,因為不知道代理目標方法的參數(shù)集合是多少,只有全局假設(shè)一對一的參數(shù)目標。
  2. RPC客戶端在網(wǎng)關(guān)中集成了大量的代理生成,無法實現(xiàn)動態(tài)更新,例如原來手動替換DLL,接口自動更新動態(tài)代理。
  3. 每一次調(diào)用都需要從大量的代理中查找指定(或模糊匹配)的方法名稱,如果存在1KW+的接口名稱,這個查找將是一個非常嚴重的瓶頸。

總結(jié)

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

文章轉(zhuǎn)自微信公眾號@dotNET跨平臺

上一篇:

.NET Core遷移指南:WebApi升級與優(yōu)化技巧

下一篇:

OpenAI API 進階使用指南01
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費