架構設計的簡單原則,你學會了嗎?

簡單原則宣言是:「簡單優於複雜」。

由於軟體架構與建築架構在表面上存在相似性,我們往往會下意識地將對建築的美學觀念遷移至軟體架構之上。對於我們親自建造的軟體架構,我們期望它如著名建築一般宏偉、精美、富有藝術感且豪華…… 總之,絕不能顯得寒酸或簡單。

團隊壓力有時也會在有意無意間促使我們走向複雜的方向。因為大多數人評估一個方案水準高低時,複雜性是一項重要的參考指標。例如,設計一個主備方案,若採用心跳機制來實現,或許大家會覺得這太過簡單。然而,若引入ZooKeeper 來做主備決策,很多人可能會認為這個方案更「高大上」。畢竟ZooKeeper 運用的是ZAB 協議,而ZAB 協議本身就很複雜。實際上,真正理解ZAB 協議的人少之又少,但這並不妨礙我們都知道ZAB 協議很優秀。

軟體領域的複雜性體現在兩個方面

結構複雜的系統通常具備兩個特點:一是組成系統的組件數量眾多;二是這些組件之間的關係極為複雜。

然而,結構上的複雜性存在第一個問題。組件越多,其中某個組件故障進而導致系統故障的可能性就越大。這個機率是可以計算出來的,假設組件的故障率為10%(即有10% 的時間不可用),那麼由3 個組件組成的系統可用性為(1 - 10%)×(1 - 10%) ×(1 - 10%)=72.9%,而由5 個組件組成的系統可用性為(1 - 10%)×(1 - 10%)×(1 - 10%)×(1 - 10%)×( 1 - 10%)=59%,兩者的可用性相差13%。

結構上的複雜性有第二個問題。某個組件的改動會影響與之關聯的所有組件,而這些被影響的組件又會繼續遞歸地影響更多的組件。這個問題會影響整個系統的開發效率,因為一旦變更涉及外部系統,就需要協調各方共同進行方案評估、資源協調以及上線配合。

結構上的複雜性有第三個問題。在複雜系統中定位問題總是比在簡單系統中更困難。首先,由於組件眾多,每個組件都有出現問題的嫌疑,所以需要逐一排查;其次,組件間關係複雜,表現出故障的組件未必是真正問題的根源。

第二個面向體現在邏輯的複雜性

當我們意識到結構的複雜性後,第一個反應或許是“降低組件數量”,畢竟組件數量越少,系統結構就越簡單。而最簡單的結構無疑是整個系統僅有一個元件,即係統本身,所有功能和邏輯都在這一個元件中實現。

然而,不幸的是,這樣做並不可行。原因在於除了結構的複雜性之外,還有邏輯的複雜性。如果某個元件的邏輯過於複雜,同樣會帶來各種問題。

邏輯複雜的元件,有一個典型特徵就是單一元件承擔了過多的功能。以電商業務為例,常見的功能包括商品管理、商品搜尋、商品展示、訂單管理、使用者管理、付款、出貨、客服等。如果把這些功能全部在一個元件中實現,那就是典型的邏輯複雜性。

假設現在淘寶將這些功能全部在單一的組件中實現,我們可以想像一下這個恐怖的場景:系統會非常龐大,可能有上百萬、上千萬的代碼規模,“clone” 一次代碼要30 分鐘。幾十、上百人維護這套程式碼,某個「菜鳥」 不小心改了一行程式碼,就可能導致整站崩潰。需求如雪片般飛來,為了應對,會開幾十個程式碼分支,然後各種分支合併、各種分支覆蓋。產品、研發、測試、專案管理不停地開會討論版本計劃,協調資源,解決衝突。版本太多,每天都要上線幾十個版本,系統每隔1 小時就要重啟一次。線上運作出現故障,幾十個人撲上去定位處理,一間小黑屋都裝不下所有人,整個辦公區都會鬧翻天。

總之,誰都無法忍受這樣的場景。功能複雜的元件,另一個典型特徵就是採用了複雜的演算法。複雜演算法導致的問題主要是難以理解,進而難以實現、難以修改,且出了問題難以快速解決。