通過Dapr實現一個簡單的基於.net的微服務電商系統(二)——通訊框架講解


首先感謝張隊@geffzhang公眾號轉發了上一篇文章,希望廣大.neter多多推廣dapr,讓雲原生更快更好的在.net這片土地上落地生根。

 

 目錄:
一、通過Dapr實現一個簡單的基於.net的微服務電商系統

二、通過Dapr實現一個簡單的基於.net的微服務電商系統(二)——通訊框架講解

三、通過Dapr實現一個簡單的基於.net的微服務電商系統(三)——一步一步教你如何擼Dapr

四、通過Dapr實現一個簡單的基於.net的微服務電商系統(四)——一步一步教你如何擼Dapr之訂閱發布

五、通過Dapr實現一個簡單的基於.net的微服務電商系統(五)——一步一步教你如何擼Dapr之狀態管理

六、通過Dapr實現一個簡單的基於.net的微服務電商系統(六)——一步一步教你如何擼Dapr之Actor服務

七、通過Dapr實現一個簡單的基於.net的微服務電商系統(七)——一步一步教你如何擼Dapr之服務限流

八、通過Dapr實現一個簡單的基於.net的微服務電商系統(八)——一步一步教你如何擼Dapr之鏈路追蹤

九、通過Dapr實現一個簡單的基於.net的微服務電商系統(九)——一步一步教你如何擼Dapr之OAuth2授權  && 百度版Oauth2

十、通過Dapr實現一個簡單的基於.net的微服務電商系統(十)——一步一步教你如何擼Dapr之綁定

十一、通過Dapr實現一個簡單的基於.net的微服務電商系統(十一)——一步一步教你如何擼Dapr之自動擴/縮容

十二、通過Dapr實現一個簡單的基於.net的微服務電商系統(十二)——istio+dapr構建多運行時服務網格 

十三、通過Dapr實現一個簡單的基於.net的微服務電商系統(十三)——istio+dapr構建多運行時服務網格之生產環境部署

十四、通過Dapr實現一個簡單的基於.net的微服務電商系統(十四)——開發環境容器調試小技巧

十五、通過Dapr實現一個簡單的基於.net的微服務電商系統(十五)——集中式接口文檔實現

十六、通過Dapr實現一個簡單的基於.net的微服務電商系統(十六)——dapr+sentinel中間件實現服務保護

十七、通過Dapr實現一個簡單的基於.net的微服務電商系統(十七)——服務保護之動態配置與熱重載
附錄:(如果你覺得對你有用,請給個star)
一、電商Demo地址

二、通訊框架地址

 

  書接上回,今天來分享一下這套電商demo的通訊部分到底是如何工作的,看看它是如何屏蔽與dapr繁瑣的溝通工作讓開發者專注於解決業務問題的。

  首先我們再回顧一下dapr的sidecar是如何與應用相互協同的。和istio類似,dapr的sidecar注入可以分為自動注冊和手動注冊,下面以手動加注解注冊的方式我們來聊一聊dapr的工作邏輯。首先當我們設置一個應用(deployment)的時候,在template-metadata配置了dapr相關注解之后,凡是安裝dapr集群的k8s會自動將dapr的sidecar注冊到我們的pod中,如下圖:

當服務啟動后,我們可以用kubectl describe po xxx的方式看到當前該pod會產生兩個容器:

  凡是了解k8s的開發人員應該知道。在同一個pod之中,container實例之間的通訊應該是基於同一個虛擬內網的,通俗的說就是兩者通訊可以直接通過localhost:port的方式,這是dapr與應用交互的基礎。和istio通過iptables 來做流量劫持讓Envoy代理可以攔截所有的進出Pod的流量,即將入站流量重定向到 Sidecar,再攔截應用容器的出站流量經過 Sidecar 處理的方案相比,dapr選擇了一個更加靈活的方式,也就是它只是主動暴露一個端口(默認3500),將是否和dapr通訊的選擇權留給了應用本身。

  當我們發起一個rpc請求時,實際上我們是通過http(or grpc這里不展開)的方式,訪問了了http://localhost:3500/v1.0/{invoke}/{servicename}/method/{path} 這么一個地址。sidecar通過解析這個地址得到遠程服務名{servicename},以及一個謂詞{invoke}以及遠程服務的endpoint:{path}。它會通過內部的dns服務名查詢servicename得到一個該服務在集群內的實例列表,通過負載均衡的方式發起一個下游調用。這個下游調用也並非直接像普通k8s應用內通過調用service name的方式去調用下游pod的container,而是訪問下游pod內的sidecar,通過sidecar再去訪問pod內的應用實例。他們之間的調用關系如圖所示:  

  通過這樣的設計,實際上應用只需要和daprd這個sidcar打交道即可。同時dapr實現了通過謂詞解析成不同的服務類型實現。比如服務間調用通過謂詞:{invoke}、狀態讀寫的謂詞是{state}、訂閱發布的謂詞是{publish}、{subscribe},幾乎所有的行為都可以分解成謂詞+服務名+endpoint這種模式(所有api可參考:https://docs.dapr.io/reference/api/),這也是實現這套通訊框架的基礎。

   所以剩下的事情就比較簡單了,dapr通訊基於http/grpc,所以我們只需要啟動一套kestrel+httpclient or grpc service/client即可簡單快捷的接入dapr。首先我們還是看看整個repo(https://github.com/sd797994/Oxygen-Dapr)的結構:

  Oxygen這部分主要是包含通用工具層、IOC依賴注入(基於autofac)、本地代理生成器ProxyGenerator。Client主要包含一些遠程服務attr標記以及客戶端代理工廠。而在Mesh這個單獨分層里主要是對Dapr的Actor實現了相關封裝、Service層比較簡單,只是在hostbuilder啟動了一個kestrel並獲取所有標記了遠程服務的接口來構建路由字典方便將我們的Application服務暴露成restapi。

  本地代理ProxyGenerator實現比較簡單,使用了微軟自帶的代理類DispatchProxy。通過Autofac依賴注入接口的時候將接口和代理類實現注冊到ioc容器中,這樣當我們通過IServiceProxyFactory.CreateProxy時實際上是從ioc容器中拿到的DispatchProxy實例,這樣調用任意該接口的方法都會被路由到DispatchProxy實例,從而實現方法攔截並最終通過RemoteMessageSender類型里的HttpClient發起對dapr的sidecar請求。

  Client層的ServerProxyFactory也比較簡單,其實就三個東西,一個是IServiceProxyFactory,這個主要用於發起對遠程rpc和actor的調用、一個是IEventBus以及IStateManager,分別用於發布事件和調用dapr的狀態管理器。

  Service.Kestrel層主要是通過啟動時由RequestDelegateFactory.CreateDelegate的方式將所有注冊為remoteservice的接口實現為其構造一個Func<Tservice, Tin, Task<Tout>>這樣的匿名委托並將其路由鍵和該委托注冊到一個全局靜態字典中。當收到請求時通過kv鍵值對的方式查詢當前key(router)對應的匿名委托,並通過ioc容器構造一個Tservice實例(為什么要請求時創建一個實例?因為這樣可以模擬MVC創建controller的方式將Tservice作為一個scope生命周期的對象創建出來,避免Tservice內部的構造函數依賴的非單例對象生命周期失效)

  整個請求收發流程如下:

    1、當客戶端通過IServiceProxyFactory.CreateProxy<IxxxServcice>()時獲取到該接口的DispatchProxy實例。

    2、實例解析各種參數后發起一個http調用,http請求localhost:3500的sidecar后等待回調。

    3、sidecar將請求組裝后發給下游sidecar並由下游sidecar轉發給pod內的應用。

    4、應用收到請求后解析path得到對應的RequestDelegate,調用RequestDelegate將請求打到具體的xxxServcice服務上,由服務完成具體的業務。

  Mesh.Dapr則是對Actor行為的一個具體封裝,由於原始的dapr sdk需要繼承BasicActor然后進行各種actor作業,我采用了另外一種方式,通過emit靜態代理的方式創建了一個Actor服務,由其代為接收actor請求后再轉發給具體的xxxServcice。同時這個Actor服務會啟動一個timer,當timer到期時會進行一次model的版本檢查,當版本變化后(一般是由於xxxServcice被調用),會通知xxxServcice繼承自基類並重寫的SaveData方法,由xxxServcice自身考慮是否需要做業務層的持久化(默認Actor代理服務會自動持久化到dapr的狀態設備里),這一步是完全異步的並不會阻塞Actor代理原方法的執行,另外在Actor的使用中,我們也盡量避免在同步調用時去讀取第三方的設備可能導致IO阻塞actor。在源碼中涉及對actor調用xxxServcice異步的支持,我主要參考了async/await生成狀態機的方式創建了一個ActorAsyncStateMachine,由該狀態機來完成actor服務調用xxxServcice的async/await實現。

  sample包含一對客戶端/服務端案例包含上述涉及的所有遠程call,大家可以多參考一下。

  Dapr原始提供了一套sdk用於遠程服務,該框架主要是用於實現rpc以及對dapr這些api的自定義封裝,當使用這套框架后我們就可以不用再考慮創建具體的webapi控制器,由iapplicationservice申明遠程服務后框架即可自動生成代理服務即可。

  該框架的實現方式當然還有諸多不完善或者我沒考慮到的地方,主要起到一個拋磚引玉的作用,另外也是通過這個來了解dapr是如何統一了我們網絡編程模型的,只有更了解dapr才能更好的使用和推廣它。慣例,歡迎fork+star:

  https://github.com/sd797994/Oxygen-Dapr

  https://github.com/sd797994/Oxygen-Dapr.EshopSample  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM