我們在之前聊過如何準備設計審閱也介紹了分散式交易,那麼讓我們把這兩者結合在一起。讓我來示範一下,如何做一個分散式交易的設計審閱。
為了避免大家忘記,我簡單列一下要點:
因此我會按照一個真正的設計審閱流程設計一個分散式交易功能,但太細節的內容會省略。
首先,我們來定義這次的使用者故事。
這次我們要實作的新功能是:送禮物。整個故事如下:
整個送禮的使用情境已經被清楚描述了,但還有些細節是沒被定義的。例如:
正如我們上面看到的,使用案例是那些在使用者故事中沒被定義清楚的細節。
現在我們可以開始畫整個設計的C4模型了。
根據使用者故事,我們可以畫出對應的上下文並且描述使用者和系統的互動。根據上下文,我們知道有幾個重點必須要討論清楚:
gift
的線Server
的行為notify
的線也許你會問,那notification service
和receivers
不用討論嗎?
答案很簡單,那些是第三方服務,我們無法控制他的行為。因此在這場設計審閱中不需要討論。
當然,如果對那些第三方服務有疑慮,那可以再舉行另一場設計審閱來討論通知系統,但那不在這場審閱的範疇。
一但我們有了上下文,我們就可以更深入那三個重點。根據C4模型,我們將其用container的方式展開。
最終我們會得到這結果。
這裡有很多設計決策,但在這時候,我們只需要清楚描述架構即可,設計決策會留到之後一併討論。
Server
收到請求後,首先先將使用者的點數扣掉。如果餘額不足就直接回nak
拒絕請求。如果餘額足夠,就將送禮請求分成數批並存入資料庫,接著將批次任務非同步的送給執行者並帶上送禮的交易序號。Server
回覆ack
表示工作正在進行。在C4模型還有兩個項目,分別是Component和Code,但這太細節了,會讓這篇文章失焦因此跳過。
我們可以從C4模型中發現很多設計決策,就像之前我提過的公式:「為什麼用A而不是B」。現在讓我們來一一檢視。
Server
分成編排(Orchestration)和處理者?讓我們從頭開始說起。
其實系統本來就已經具備一對一送禮的功能,因此最簡單的方法是跑一個迴圈對所有收禮者一對一送。
當收禮者數量少時,這不會有問題,可是一但收禮者的數量增加,對效能是一個嚴重挑戰。根據計算,送一個禮物需要100-200毫秒,也就是說,當送給十個人,就會達到秒級了。這完全無法接受。
看起來批次作業無可避免,因此我們稍微改造一下。
從圖上可以發現,編排已經出現。但是,所有跟處理者的溝通是以同步的方式進行,且通知要等所有送禮結束由編排發出。
看起來好像能解決效能瓶頸了,對嗎?其實這沒有比較好。
讓我們算個簡單的數學。假設我們要送給一千個人,那我們該怎麼設定批次的大小和數量?
為了在秒級內完成,批次最大只能10個,且100個處理者要同時叫起來處理任務。這是一個嚴苛的挑戰,要能夠瞬間產生100個處理者不是件容易的事。
所以同步看起來行不通,那如果非同步呢?
在第二次嘗試中,我們將編排改成編舞。
因此送禮者可以快速得到回應且送禮過程可以保持流暢。但,真的是這樣?
如果中間的處理者故障會發生什麼事?整條鏈就斷了。有些收禮者因為沒收到通知所以沒有感受,但對送禮者來說,送到一半等於點數已經被扣了部分,卻沒收到通知。更糟的是,回到一開始的使用案例,部分成功不被接受。
編舞相較於編排會有更好的效能和更好的擴充性,但也具有更複雜的流程控制。因此,在這個案例下編排會更加適合。
所以,讓我們來實作一個非同步的編排。
這個架構馬上會遇到兩個問題:
送通知的問題還容易解決,就像C4模型中描述的,最後一個處理者送就好。儘管如此,送到一半餘額不足在非同步下基本無解。
綜上所述,最後我們採用半同步的做法。
首先,編排先判斷餘額是否足夠,為了避免競爭,如果足夠就直接將點數扣除。因此,處理者只需要負責送禮,不需要處理點數問題,甚至根本不用確認餘額。
在這樣的架構下,會有一個大麻煩。
怎麼解決處理者失敗?
如果是因為資料庫阻塞造成的,也許重試幾次就好。若是功能錯誤之類無法重試的錯誤,該怎麼復原?
就結果來說,需要一個額外的監控機制,定時確認那些非同步任務的狀態,並且重試那些可以修復的問題,若是無法修復就必須通知人類介入處理。
在之後的文章中會介紹事件驅動架構下常見的設計模式。
總結一下,這個送禮被拆分出幾個核心重點:
在這篇文章中我們用實際例子討論了設計分散式交易的挑戰,並探討不同的決策:
這些都是典型分散式交易的取捨,且這些面向大大主宰整個系統設計。