爲什麼你的微服務不對勁?

文 | 少個分號 (轉載請註明出處)

關注公衆號:DDD 和微服務

微信號:linksgo2016

同名知乎:少個分號

在雲原生的時代,微服務已經變成了一個默認選項。但大部分開發者反饋,他們的微服務總是有這樣那樣的問題,明明是一個非常好的實踐,但是感覺帶來的問題,比解決的問題還多。

今天就來聊一聊,微服務爲什麼讓我們覺得不對勁。

最根本的原因,還是我們對微服務概念本身的定義和理解有所偏差。微服務,不是一個全新、炫酷、能解決很多問題的架構,而是一個極其昂貴的分佈式系統。如果用一句話去概括,它可以被描述爲:

基於松耦合通信協議(HTTP)的分佈式應用系統。

這也是我們在 DDD 社區給微服務的一個基本定義。從這個定義來說,它有兩方面的含義。

第一,首先是一個分佈式系統。既然是一個分佈式系統,就會帶來分佈式系統各種各樣的問題和不得不做出取捨。

第二,松耦合協議。是相對於 SOA 這種面向服務的架構來說,使用 HTTP 等松耦合的通信協議取代 RPC,做到獨立開發和發佈。可以把它看作爲 SOA 架構的變種,這也是維基百科給它的定義。

那麼,既然是一個分佈式系統,而且會帶來這麼多的問題。爲什麼會流行起來呢?

實際上,在做技術選型的時候,有很多的制約條件。微服務的流行,實際上是大廠隨着開發人員的擴大和系統容量的要求不得已將單體的應用劃分爲可以獨立開發、部署的單元,讓數量巨大的開發人員能有效協同。

所以說:

大廠選擇分佈式系統可不是爲了炫酷,而是不得已爲之。

對於三五個人幾條槍的小開發團隊來說,微服務的使用從收益和付出來看並不划算。並且我們有的時候沒有意識到微服務相對於單體架構來說,它的成本是巨大的。

  1. 分佈式系統代價

對於客觀原因,我們又不得不採用分佈式系統來解決我們的問題,當我們意識到微服務是一個分佈式系統的時候,就可以使用分佈式系統的一些原理,指導和解決在做微服務開發的過程中遇到的各種問題。

下面聊一聊微服務系統中有哪些代價和問題需要我們去解決。

分佈式鑑權

首先,微服務被拆分出來之後,帶來一個明顯的問題,就是分佈式的授權體系。這是一個很容易被忽略的代價,即使是隻有由有兩個服務組成的最小微服務系統,也不得不提供一個單獨的鑑權中心。

最簡單的實現方式,就是共享一箇中心的會話狀態庫(實際上這不是分佈式授權)。如果是基於 Session 的鑑權系統,分佈式化後可以使用 SSO 單點登錄,通過維護全局會話、本地會話的方法解決分佈式授權的問題。

如果使用 token 的方式鑑權,可以使用 OAuth2 等開放授權協議進行授權。但分佈式授權系統會面臨一個矛盾:

性能和實時可撤銷的矛盾。

如果每一次請求都去授權中心檢查授權會帶來性能問題,授權中心故障後業務也會受到影響。但是如果使用本地授權(例如 JWT),在一定時間內,無法撤銷發送的憑證。這剛好就是 CAP 定理所說的,在選擇分區容忍性的條件下,一致性和可用性之間存在矛盾。

如果不使用分佈式的思路去設計鑑權體系,就會出現這樣或者那樣的毛病,還不知道爲什麼會這樣。

有分就需要合

分佈式系統第二個問題矛盾就是:

服務拆分的越小,就越是需要有地方編排。

微服務系統總是需要一個編排者,但是大家不知道它叫什麼。當訂單和產品屬於不同的服務,就必須要通過某個服務來整合他們的數據和流程,這就是編排。

很多架構師不願意承認這一點,他們總是希望服務是可以端到端獨立交付的,如果使用編排層,往往會破壞團隊端到端獨立交付的能力。

但是編排的訴求不會憑空消失,而是進入了前端或者領域層。前端編排具有非常大的不確定性,泄漏到領域層就會喪失服務的複用能力,這都是爲後續暴雷埋下的坑。

上圖是一個企業即時通訊軟件的架構。在架構早期,用戶服務和會議服務都是被直接暴露出去,但是用戶業務和商戶後臺的業務對複用的邏輯造成了衝擊。由於用戶服務是單獨的一個團隊在維護,不得不妥協成上面的樣子。未來可能暴雷的地方在於,用戶服務中的 user 模塊和會議服務中的 user 模塊會造成衝突。

分佈式事務

分佈式事務是微服務系統中另外一個比較麻煩的問題。Fischer 在他的論文中,證明了在總會存在故障的分佈式系統中,達成實時共識是不可能的。

CAP 定理就是這個證明的進一步演繹:分區容忍性、一致性、可用性只能同時選擇其二。

一致性和可用性的矛盾。

但是有些架構師對 CAP 定理的錯誤理解是,只能選擇其二,忽略了 “同時” 這個關鍵要素。雖然分佈式系統不可能達成實時共識,但是能達到近似的共識。如果達成共識的時間足夠短,就能當做取得共識。

這就是分佈式事務協調器的基本原理。

團隊治理

我們最開始使用微服務的時候,其中有一項訴求是開發服務的團隊能夠實現端對端的交付。但是有意思的是,我們往往沒有意識到這其中有一個矛盾,如果我們需要做到能力的複用,就必然不可能實現端到端的交付。舉個例子來說,如果用戶服務被多個場景使用,端到端團隊怎麼設計呢?

追求複用能力和端到端交付之間的矛盾。

分佈式查詢

微服務擁有各自的數據庫並存儲在不同的地方,這樣可以降低數據庫的壓力,做到垂直分庫。而代價是,如果業務中有跨數據庫聯表,並需要展示在相同的視圖上,那麼對於查詢,帶來一個非常大的困擾。

如果是同一個數據庫,可以通過列表的方法,或者構建視圖的方法來解決這個問題,但是在分佈式系統下(尤其是分佈式應用系統,不是分佈式數據庫的設計),就不得不使用其他的方法來解決這個矛盾。

集合的遠程連接訴求和笛卡爾運算失效的矛盾

解決這個麻煩的方法有:

  1. 使用數據冗餘,降低範式。

  2. 使用 IN 語句,並帶上讓索引起作用的額外過濾條件,避免全表掃描。

  3. 使用搜索引擎,通過空間換取時間。

其他工程問題

當然,分佈式系統還有各種各樣其他的問題,當服務被部署到不同的服務器節點上,需要追蹤調用關係去解決問題時,debug 的難度就陡然上升。

其次,每一個服務的業務邏輯,會把日誌放到不同的日誌當中,無法順序的去跟蹤一個完整的流程和結果。

另外,也有版本發佈的問題,一個服務在發佈時,強烈的依賴其他服務的更新,會造成發佈過程中的各種問題。

  1. “糟糕” 微服務的原因

即使分佈系統有這樣那樣的問題,但是設計一個良好的分佈式系統也是可能的,它的前提有兩個:

  1. 對系統中服務承擔的角色做合理的設計。

  2. 服務能夠獨立的提供能力,避免將服務劃分得過小。

對於第 1 個前提,可以使用一個形象的比喻。如果我們服務承擔的角色不夠明確,比如缺乏編排的角色,就好比我們去政務中心辦理業務的時候,缺乏一個接待員。就需要依賴辦事的人通曉辦理業務的所有流程,自己跑完全部的窗口。

對於第 2 個前提,也有另外一個比喻。如果一個服務不具備獨立提供能力,那麼就會頻繁的依賴其他的服務、數據耦合、分佈式事務等等一系列問題,就像一個無法獨立完成任務的實習生,總會在辦公室到處求助一樣。

參考資料

[1]. Michael J. Fischer, Nancy A. Lynch, and Michael S. Paterson. 1985. Impossibility of distributed consensus with one faulty process. J. ACM 32, 2 (April 1985), 374–382. DOI:https://doi.org/10.1145/3149.214121

[2]. An Illustrated Proof of the CAP Theorem.https://mwhittaker.github.io/blog/an_illustrated_proof_of_the_cap_theorem

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