微服務架構的陷阱:從單體到分佈式單體

微服務架構的陷阱:從單體到分佈式單體

你好,我是看山。

前面咱們聊了架構的演進過程,提到單體架構、SOA 架構、微服務架構、無服務架構。整個過程如下圖:

架構演進過程:單體架構、早期服務化、SOA 架構、微服務架構

目前無服務架構還未成熟,只能滿足一些簡單場景。所以大家在設計軟件架構時,首選還是微服務架構。然後我們又聊了聊如何把單體架構改造爲微服務架構,推薦採用絞殺模式,一步一步的實現系統微服務化。

在這個過程中,我們會碰到微服務架構的一個大坑:分佈式錯覺,即將分佈式當成了微服務的全部(充要條件)。

原因

出現分佈式單體的主要原因在於,只是用進程間的遠程調用替換進程內的方法調用。

模塊 A 與模塊 B 之間的通信方式

從上圖可以看出,單體架構在模塊 A 與模塊 B 之間的請求是通過進程內通信(通常是方法調用)實現的;在微服務架構中,兩者之間是通過 REST 或 RPC 調用。拋開進程和消息通知機制的差異,兩種架構中模塊 A 與模塊 B 之間的通信形式完全一致:

單體 / 微服務架構中模塊間通信方式

在這種情況下,模塊 A 與模塊 B 耦合在一起,任何一方變更請求契約(方法簽名或接口參數),另外一個都必須同步修改。更糟糕的是,由於微服務架構服務之間是通過網絡通信,由於其不可靠性和不穩定性,大大增加了出錯的概率,使模塊之間的調用關係更加脆弱。

模塊 A 與模塊 B 之間的網絡請求是同步調用,請求過程中會佔用一個網絡連接和至少一個線程,如果模塊 A 與模塊 B 所在的服務的承壓能力不同,很有可能模塊 B 所在服務被打滿,後續模塊 A 的請求會阻塞等待,直到請求超時。

那又是什麼原因讓大家沒有意識到這種方式不妥呢?原因有兩個:

  1. 想要在微服務架構中實現單體架構中模塊間的關係;

  2. 想要在分佈式應用中實現數據的強一致性。

針對於微服務架構中的數據一致性問題,可以參考 關於微服務系統中數據一致性的總結

下面我們重點說說如果解決第一個問題。

方法

對於模塊之間的關係,主要在於通信模式,對於查詢請求,由於數據依賴,模塊之間的耦合是天然的,我們這裏要解耦的是數據變更(增、刪、改)時的模塊調用。

類比一下現實,我們如果想要通知某些人一個消息,會怎麼處理?一般來說,有兩種方式:

  1. 點對點主動通知,直接找到這個人,給這個人打電話,不通繼續打,保證對方能夠收到消息;

  2. 點對面廣播通知,比如,羣裏發給公告、公司的告示欄等,這種方式需要消息接受者主動查看公告信息。

這兩種方式對應了我們系統設計中消息傳遞的兩個模式:指令(Command)、事件(Event)。

指令(Command)和事件(Event)

command 與 event,指令與事件

指令(Command)是表示從發起者(source)向執行者(destination)傳遞(send)一個必須執行某個動作(action)的請求(request)。這個模式有如下特點:

事件(event)是表示由生產者(producer)發佈(public)一個已經發生的事情,表示行爲(action)已經發生,某些狀態(status)發生了改變,消費者(consumer)訂閱這些事件,然後做出響應。這個模式有如下特點:

指令與事件之間的區別

由於請求模式的不同,在依賴關係上就會發生改變:

在指令模式中,模塊 A 調用模塊 B,屬於直接調用,模塊 A 需要依賴模塊 B;在事件模式中,模塊 A 把事件發送給消息中間件,其他需要訂閱事件的服務,直接從消息中間件獲取,這種會產生依賴倒置,模塊 B 依賴模塊 A。這是解耦模塊 A 與模塊 B 很好的方式。

重新定義微服務

我們再回過頭來看看我們的問題:

單體 / 微服務架構中模塊間通信方式

此時我們會比較清晰,由於全系統中使用了指令模式,上次調用者依賴下層,由於是同步請求,依賴會發生傳遞,這種依賴傳遞,將整個系統耦合在一起,一處修改,處處變動,也就是我們在抨擊單體架構時常說的牽一髮而動全身。

此時,我們就可以藉助事件模式,將依賴鏈條打斷。但是需要注意,不要矯枉過正的全部改爲事件模式,那將會是另一個火坑。一般我們會將系統改造成下面的樣子:

重新定義微服務

根據業務具體情況,我們可以歸納一下改造結果:

  1. 服務 A 接到的請求可能是事件或指令;

  2. 服務 A 會向服務 B 發送指令,也會向消息中間件發送事件;

  3. 服務 B 接到指令後開始執行,執行完畢後,可能會向消息中間件發送事件;

  4. 服務 C 定於事件,從消息中間件接到消息後處理,它可能發送事件或指令。

需要注意的是,每個服務內部還有有一些操作。抽象一下,整個系統中的指令、事件、操作如下圖:

指令和事件的定義

架構設計的過程不是非此即彼,全部指令會造成耦合,全部事件會致使開發難度提升以及邊界不清。我們需要理性的看待兩種模式,做到不偏不倚。

文末總結

本文從分佈式單體陷阱展開,講述了分佈式錯覺帶來的問題,然後通過事件、指令兩種模式相結合的方式解決問題。微服務是目前比較完善的架構風格,從單體到微服務架構,是要實現架構的升級,所以調用模式不會一成不變。這個陷阱,也是我們在做新系統時需要避免的。


本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/b7my-EHc0fGOE3veeQyxBg