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

  • 2021 年 4 月 16 日
  • 筆記

  之前的章節我們介紹了如何通過dapr發起一個服務調用,相信看過前幾章的小夥伴已經對dapr有一個基本的了解了,今天我們來聊一聊dapr的另外一個功能——訂閱發布

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

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

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

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

附錄:(如果你覺得對你有用,請給個star)
一、電商Demo地址

二、通訊框架地址

  慣例我們還是再老生常談一下什麼是訂閱發布,訂閱發布是根據設計模式之觀察者模式發展出來的一種軟體系統設計思想,它的核心是指「多個對象間存在一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新」。假設一個系統有ABC三個模組其中BC依賴於A,當A進行改變後需要A主動調用BC進行相應改變,而觀察者模式則將A的控制權剝離,A改變之後只是發送一個事件給消息匯流排「我改變了」,BC通過預先訂閱該主題而獲取到A的狀態變化,再進行自身的狀態變更。

強耦合調用模式

通過消息中間件訂閱發布模式

  聰明的同學應該發現了,通過訂閱發布其實我們是將以往強耦合的ABC通過巧妙的控制權轉移的方式進行了解耦,由之前的BC依賴於A改為了BC依賴於消息組件,通過消息組件接受到A的消息後進行分發,這樣設計系統的目的當然有好有壞,好處是這會大大提高A的吞吐量,假設以往操作ABC總耗時300ms平均單個操作耗時100ms,通過解耦後A耗時100ms後就可以馬上返回執行緒。那壞處是什麼呢,由於對BC進行了解耦往往狀態的一致性就得不到保障了。當ABC處於同一個粗粒度的原子操作里(比如資料庫事務操作、比如lock鎖),我們很容易控制ABC的強一致性,ABC有一個操作失敗可以很輕鬆的進行回滾而不用影響我們持久化設備的數據一致。另外由於需要依賴第三方中間件,整個系統的健壯性是會有一定影響的。另外還需要考慮訂閱方消費失敗、異常後如何處理。關於這部分內容這裡我們就不展開了,如果大家確實感興趣,推薦大家看看中國開源作者@楊曉東寫的.netcore分散式一致性解決方案CAP,地址://github.com/dotnetcore/cap

  OK,老生常談的部分嘮完,咱今天就來嘮嘮在Dapr中如何實現訂閱發布的。通過上面的部分大家應該知道如果要實現一個進程間的訂閱發布系統,我們需要準備很多東西,其中一個是事件匯流排,事件匯流排的作用是讓事件發布者可以通過該模組進行事件發布。第二個需要選型一個消息組件,第三需要訂閱器對訂閱特定類型組件的技術支援。由於每一種組件其協議和介面實現方式都不同,在傳統的訂閱發布設計中我們往往需要對某種特定類型的消息組件在基礎設施層進行相應的SDK集成,通過為業務層提供事件匯流排介面和訂閱介面來進行技術解耦。就算做到了業務系統中不耦合技術實現,往往我們也很難替換消息組件。而Dapr在這方面為開發者集成了相當一部分的主流訂閱發布組件(通過該支援列表可預覽支援)。同時在業務層面是完全基於http+謂詞的形式來實現訂閱發布的。這就大大降低了引入SDK產生的成本。

  訂閱發布的API如下:

POST http://localhost:<daprPort>/v1.0/publish/<pubsubname>/<topic>[?<metadata>]
GET http://localhost:<appPort>/dapr/subscribe

  稍微說一下這兩個介面的含義,第一個介面告訴sidecar,我們將會調用某個已申明的類型為pubsub的component發送我們的事件到特定的topic

  第二個介面是告訴sidecar我們當前這個服務會訂閱哪些topic,我們提供的訂閱器入口地址是什麼,我們只接受哪一個component發布的數據

  也就是說和服務調用一樣,我們只需要一個weapi+httpclient就可以實現一個訂閱發布模式而,其他的一切都交給dapr好了。

  現在我們來看看如何實現它,首先我們還是需要選一個特定的訂閱發布組件,這裡我就選擇redis,通過redis5.0新增的stream來做訂閱發布。搭建redis這裡不贅述,搭建好之後,我們需要創建一個dapr的特定CRD Component來申明引用該組件,其申明yaml文件如下

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: pubsub
spec:
  type: pubsub.redis
  version: v1
  metadata:
  - name: redisHost
    value: redis.infrastructure.svc.cluster.local:6379

  其中的type是由Dapr預定義的,可以參考這裡,不要隨意改變。metadata是對組件的一組描述包括其地址、帳號密碼等等,由於是演示這裡我就直接使用k8s創建了一個無密碼的redis pod並暴露其6379埠到svc。當我們創建好並通過kubectl apply -f x.yaml後,可以在dashboard的Componsents頁面觀察是否已經創建成功

  基礎設施準備完畢,接下來就是打開我們的項目看看如何實現訂閱發布了。首先我們還是要把上一章的解決方案打開,上一章不是通過client發起一個對service的調用嗎,今天我們反著來演示,我們在client創建一個訂閱器,當clientsample調用servicesample時讓servicesample發布一個事件,由該訂閱器訂閱並列印出文本到控制台。

  首先我們在clientsample創建一個訂閱器,訂閱器類必須繼承ieventhandle介面,其訂閱方法體必須添加EventHandlerFunc註解並申明需要訂閱的主題(topic),訂閱器接收參數必須以EventHandleRequest<T>的方式接收,否則反序列化器可能會收不到請求。最後ack必須以DefaultEventHandlerResponse.Default的方式返回,否則事件匯流排會認為本次訂閱器消費失敗,會重複推送。

   需要在我們的hostbuilder里註冊這個對象到ioc容器:

  接著我們在RPC介面項目創建事件Data HelloEventData,其中之包含一個演示用的words欄位(圖略)。

  接下來我們在上一章的HelloServiceImpl中注入一個事件匯流排,並發送事件:

 

   一切就緒,我們重新按照上一章的內容打包並部署,然後開啟postman和控制台日誌觀看結果,可以看到我們的clientsample發起一個服務調用後,我們的serversample回調並發送了事件,而clientsample成功訂閱到了該事件並消費掉了。

 

   今天的分享到此為止,demo只是對dapr訂閱發布最基礎功能的演示,真正到生產環境還需要客服諸多問題來提高系統健壯,系統建設是一個長期持續的過程。歡迎大家評論區留言討論~ 下期我們將講一下dapr里如何做狀態管理以及actor模型