架構設計的簡單原則,你學會了嗎?
簡單原則宣言是:“簡單優于復雜”。
圖片
由于軟件架構與建筑架構在表面上存在相似性,我們往往會下意識地將對建筑的審美觀念遷移至軟件架構之上。對于我們親自構建的軟件架構,我們期望它如著名建筑一般宏偉、精美、富有藝術感且豪華…… 總之,絕不能顯得寒酸或簡單。
團隊壓力有時也會在有意無意間促使我們走向復雜的方向。因為在大多數人評價一個方案水平高低時,復雜性是一項重要的參考指標。例如,設計一個主備方案,若采用心跳機制來實現,或許大家會覺得這太過簡單。然而,若引入 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 個小時就要重啟一次。線上運行出現故障,幾十個人撲上去定位和處理,一間小黑屋都裝不下所有人,整個辦公區都會鬧翻天。
總之,誰都無法忍受這樣的場景。功能復雜的組件,另一個典型特征就是采用了復雜的算法。復雜算法導致的問題主要是難以理解,進而難以實現、難以修改,并且出了問題難以快速解決。