架構設計 十五字訣

架構設計 十五字訣

軟件架構設計,不是三言兩語能說清楚的。只是筆者一直也抽不出時間靜下心來去長篇大論。本文,就暫且以一個 “聽” 來的故事,簡單講述一下筆者對於軟件架構的設計心得——架構設計,十五字訣:

範圍與目標

假設與約束

風險與代價

一、“聽” 來的故事

這個聽來的故事是這樣的:一個 NMS(Network Management System,網絡管理系統)的 HA(High Availability,高可用性)是冷備方案,如圖 1 所示。 

圖1 冷備系統示意

圖 1 中,NMS1 是主用(master),NMS2 是備用(slave),並且主備方案選用的是冷備方式。所謂冷備,簡單地理解,就是 NMS2 並沒有啓動,待到 NMS1 不能工作時,NMS2 纔會(由人工或者其他系統)啓動,接替 NMS1 的工作。

NMS1 與網絡設備之間的網絡鏈接記爲 L1,NMS2 與網絡設備之間的鏈接記爲 L2。L1 穿越了一個網絡 N1,L2 穿越了另一個網絡 L2。

現在的問題是:即使 NMS2 自身可以完美地接管 NMS1 的工作,但是,當 NMS2 試圖接管工作時,如果 L2 不通怎麼辦?

假設 NMS2 自身接管工作需要 10 分鐘,這也就意味着從 NMS1 停止工作到 NMS2 開始工作,中間會有 10 分鐘的業務中斷——這一點,用戶可以接受。但是如果 L2 中斷了,其修復時間需要一天,那麼業務中斷時間將變爲 24 小時——這一點,用戶是萬萬不能接受的。

所以,用戶期望 NMS2 能夠實時檢測其與網絡設備之間的鏈接狀態,如果出現中斷,能夠報一個告警(Alarm)給用戶。如此一來,用戶就可以及時修復網絡,能夠有效保證將來系統倒換(NMS2 接管 NMS1)時的業務中斷時間。

客觀地說,用戶的期望非常自然也非常 “合理”,然而,NMS2 卻是一臉懵逼:我連啓動都沒有啓動,我該如何檢測 L2 的連通性?又該如何給你報告警?於是 NMS2 一臉委屈、義正言辭地拒絕了客戶的期望。

客戶覺得 NMS2 說的非常有道理,大聲地回了一句:

傻逼!

作爲甲方爸爸,客戶罵一句 “傻逼”,那都是輕的。真把客戶惹毛了,他反手一個投訴單寄到公司,NMS2 恐怕就不止是傻逼這麼簡單——也許十年以後,NMS2 還會在那喃喃自語:曾經有一份期望擺在我面前,我沒有珍惜 ...... 如果老天能夠再給我一次機會,我一定會去寫那一萬行代碼 ...... 

這個故事是筆者在聽人傳聞的基礎上,又做了一些演繹,不過總體上來說,還是比較 “真實” 和典型的。說它典型,是因爲站在不同的角度,甲方(NMS 的購買方)和乙方(NMS 的供應方)對於同一件事情(冷備方案),有着截然不同的理解和訴求,而且雙方都覺得自己有道理,都覺得對方是傻逼。

筆者以爲,要化解這種不可調和的矛盾,就需要說清楚軟件架構的十五字訣。在正式闡述十五字訣之前,爲了更好地描述和理解,筆者先簡述一下 HA 方案的冷備、熱備、雙活等概念。

二、HA 簡述

由於篇幅和主題原因,本文只是簡單介紹一下 HA(High Availability,高可用性)方案的基本概念和基本分類。

HA,顧名思義,乃是儘量保持一個系統一直可用,就像喝酒一樣,去努力追求無終止喝或者一直喝。

喝酒的 HA,拼的是個人酒量。而軟件系統的 HA,拼的是 “羣毆”:一個系統倒下了,另一個系統站起來。所以,在軟件系統的 HA 方案中,至少有兩個 Server,如圖 2 所示。 

圖2 軟件系統 HA 方案示意

需要特別說明的是圖 2 中的 “data”。一般來說,軟件系統服務端(Server)響應客戶端(Client)的請求,總會改變其內部數據,而一個服務端(比如 Server_2)接管另個一服務端(比如 Server_1)之前,兩者之間需要保證數據一致,否則系統的運行會出錯。

在保證系統正確運行的基礎上,根據服務端的工作方式,HA 分爲兩大類:主備(active-passive)、雙活(active-active)。

主備方案,就是有兩個 Server,一個作爲主服務(master)在工作,另一個作爲備服務(slave),不工作,待到 master 出現故障不能工作時,slave 會接管 master 的工作,同時自己也就變成了 master。

**說明:**這種情況稱爲 1:1 備份(1 主 1 備),還有 n:1 備份、n:m 備份等等。本文僅以 1:1 備份爲例進行講述,其他情況同理。

根據 slave 的啓動情況以及 master-salve 之間的數據同步情況,主備方案又細分爲:冷備、溫備、熱備等三種方案,如表 1 所示。

表 1 主備方案分類 SxIfxv

如果從倒換時間來衡量三種主備方案,通過表 1 可以看到,熱備倒換時間最短(不考慮其他因素的話,熱備倒換時間爲 0),溫備倒換時間較長,冷備倒換時間最長。

當然,衡量主備方案的指標不僅僅是倒換時間,還有其他因素,這個我們下文還會涉及,這裏暫且不表,而是繼續討論 HA 的另一個方案:雙活。

雙活(active-active),顧名思義,就是沒有主備一說,兩個(也可以是多個)Server 不分主次,同時工作,如圖 3 所示。

圖3 軟件系統雙活方案示意

圖 3 與圖 2 相比,多了一個 LB(Load Balance,負載均衡器),這是因爲 Server_1 與 Server_2 是同時在工作。既然同時在工作,那麼面對衆多 Client 的請求,兩者總得有所分工,而負載分擔則是一種比較好的分配方式。

LB 不是本文的重點,重點是兩個 Server 在同時工作,這也就意味着兩者的數據需要雙向實時同步。

雙活需要數據實時同步,熱備也需要,兩者非常容易弄混。確實,它們有一定的共同點。

(1)雙 Server 同時啓動

(2)雙 Server 數據實時同步

不過仔細分析,雙活與熱備的不同點也非常顯著的:

(1)數據同步的方向不同。熱備的方向是 master 到 slave;雙活的方向是互相數據同步

(2)工作方式不同。熱備,只有一個 Server 在工作;雙活,兩個 Server 同時在工作。兩者的工作方式,如圖 4 所示。 

圖4 熱備與雙活關於工作方式的區別

對於圖 4,再次強調一點:熱備與雙活不取決於 Client(貓咪)的數量(雖然圖 4 中,熱備的 Client 是 1 個,雙活的 Client 是 2 個),而是取決於有幾個 Server 在同時工作(注意,圖 4 中的 Server,不是狗,而是狗的 **)。

爲了更加清晰地看出雙活、熱備以及其他 HA 方案的異同,HA 方案小結如表 2 所示。

表 2 HA 方案小結

YMHFIj

表 2 對 HA 方案做了一個簡單小結,但是它與架構的十五字訣又是什麼關係呢?

三、十五字訣

我們將目光再投向那個聽來的故事。故事中的甲乙雙方互相罵對方是傻逼,一個行於言表,一個默唸於心。如果乙方能夠將軟件架構的十五字訣跟甲方解釋清楚,兩者都基於十五字訣理解問題,那麼雙方會不會繼續沒羞沒臊地在一起玩耍呢?

3.1 範圍與目標

範圍與目標,有兩個視角。一個是設計者(乙方)的視角,他需要給出其設計產品的範圍與目標。

連宇宙都是有限的,所以一個軟件設計,如果沒有明確它的範圍,或者是因爲範圍本身很明顯,衆所周知,不需要明示,但是更多的原因是

沒搞明白

彷彿自己無所不能!

與範圍形成強烈對比的是 “目標”,一個沒有明確架構目標的設計,就是

耍流氓

需要澄清的是,雖然我們說,不能爲了指標而忘了目標,但是軟件架構的目標,絕大多數需要承載於指標。數字出架構!這裏的數字,既指設計的輸入需要量化,也指設計的輸出(指標)也需要量化。

在實際的設計中,沒有明確範圍與目標的案例,比比皆是,這裏就不再贅述了。

相比於設計者的視角,另一個視角則往往容易被忽略,不過從某種意義上講,它卻更加重要,那就是:需求者(甲方)視角。

甲方視角與乙方視角,往往並不相同,以本文的故事爲例,如圖 5 所示。

圖5 不同視角中的冷備方案

圖 5 中,乙方視角中的冷備,其範圍僅僅是 NMS1 與 NMS2 本身的主備倒換(目標是倒換成功率、倒換時間等),而甲方視角中,還多了一個 NMS2 與設備之間的鏈接 L2 的連通性檢測(目標是中斷多少時間內能夠檢測出來,等等)。

雙方視角不一致,沒有關係,重要的是要把不一致明確說出來。除了有明確的文字說明,從架構圖的角度來說,系統的上下文有時候也需要明確標識出雙方視角中的範圍與目標,如圖 6 所示。 

圖6 NMS 冷備方案的上下文示意圖

圖 6 並不是一個標準的規範的系統上下文視圖,它只是想重點表達:關於 L2 的連通性檢測,需要另外一個服務或者另外一個系統來完成。

除了表達範圍和目標以外,系統上下文視圖,有時候也可以表達系統的依賴,或者說系統的假設和約束。

3.2 假設與約束

一個系統只要不是太簡單,總免不了與他系統打交道,包括與環境、人打交道,或者一個系統內部,也存在服務 / 模塊之間互相打交道的事情。既然打交道,就存在依賴。

在軟件架構的語境中,“依賴” 是一箇中性的詞彙。A 依賴 B,並不是說 A 就是 B 的 “媽寶男”,而很可能僅僅是 A 調用了 B 的一個接口而已。

有些依賴是已知的、可控的,有些依賴是未知的、或者不可控的。對於前者,需要做好依賴的設計,比如接口抽象、服務解耦等等,而對於後者,則需要設計好假設和約束。

假設是對未知依賴的一種預判。比如決定明天去爬山,是基於明天不會下雨的假設。

約束是對不可控依賴的一種合約,這種不可控,可能已知,也可能未知。比如對於一個不防水的手機,它也不能自己會不會掉進水裏,但是它可以宣稱:只要進水了,概不負責。

假設和約束這兩個概念,也有一定的混淆性,如圖 7 所示。 

圖7 告警管理系統示意圖

圖 7 是一個告警管理系統(AlarmMgr)示意。網絡設備由於多種原因(比如單板重啓、端口損壞)會給 AlarmMgr 發送各種各樣的告警。一個軟件系統的處理能力是有限的,而網絡設備發送告警的頻率卻有高有低,這與網絡規模(網絡中包含多少設備)有關,也與網絡當時所處環境、網絡上所跑的業務也有關,比如有的網絡會發送 100 條 (告警)/ 秒,有的網絡會發送 500 條 / 秒。

對於 AlarmMgr 來說,它不能將自己的能力設計爲無限大或者超級大(比如能夠處理 100 萬條 / 秒),畢竟它受限於很多客觀條件。但是,AlarmMgr 總得有一個設計指標,比如能夠處理 200 條 / 秒。那麼,這個 200 條 / 秒,是 “假設” 還是 “約束” 呢?

都可以!只是其後果不一樣!

將 200 條 / 秒設計爲假設,如果實際爲 300 條 / 秒,顯然 AlarmMgr 無法有效處理告警,此時的責任在乙方——AlarmMgr 的提供方。

將 200 條 / 秒設計爲約束,如果實際爲 300 條 / 秒,雖然 AlarmMgr 無法有效處理告警,但是此時的責任在甲方——AlarmMgr 的需求方。

因爲約束本質上是一個合同條款,是對甲方的約束。只有滿足約束條件,乙方纔能承諾正確工作,否則,乙方概不負責。說白了,這有點像

把醜話說在前面

不管好壞,就是這麼回事,甲方你要願意就願意,不願意拉倒!

而假設卻不具備合同條款的性質,它只是乙方自己對未知條件的一種認識。如果實際條件突破了乙方的假設,從而造成系統不能正確工作,那是乙方自己的責任,甲方概不負責。從某種意義上說,假設,是乙方對未來的一種賭博,那就要

願賭服輸

一個人即使貌美 “如花”,但只要婚前以真面目示人,甲方就不能婚後職責,這是約束。而如果是洞房之夜才能揭開紅蓋頭,甲方就可以跳起來罵娘——這要怪只能怪乙方對甲方的容忍度假設得太過美好。 

再回到本文所說的故事中,如圖 8 所示。

圖8 L2 的連通性保證

圖 8 的重點是:L2 的連通性,到底是假設還是約束?如果是約束,那麼乙方就不必提供 “連通性檢測” 服務,即使 L2 中斷了,那也是甲方的責任。如果是假設,那乙方就有必要提供 “連通性檢測” 服務——既然賭 L2 是連通的,那或者對 L2 的中斷負責,或者就是去老老實實地檢測 L2 的連通性。

在乙方假設 L2 是連通的前提下,如果乙方不願意去檢測 L2 的連通性,那它就得冒着 L2 中斷的風險。所以,從風險的角度看問題,

假設,就是乙方的風險

約束,就是甲方的風險

3.3 風險與代價

對於未知的、不確定的依賴,無論是將其定義爲假設還是約束,甲乙總得有一方承擔風險。難道沒有更好的方案,使得雙方都不承擔風險嗎?沒有!

因爲,

不承擔風險,就得承擔代價!

我們還是以 AlarmMgr(告警管理系統)爲例,乙方假設告警發送頻率是 200 條 / 秒,它就得承擔着如此風險:如果實際頻率大於 200,系統就不能正確工作,那就可能被甲方啪啪啪...... 打臉 

如果乙方視圖消減這個風險呢?比如將告警發送頻率假設爲 1 億條 / 秒(這個假設絕對足矣),將來面對任何網絡,都不會出現被打臉的風險。

世界上沒有免費的午餐,更沒有零代價的風險消減。如果要實現處理能力爲 1 億條 / 秒的高精處理系統,其實現代價可能高到無法想象。這個時候,恐怕就不是甲方打臉了,而是自己對自己啪啪啪 ......

而且,這個代價不僅僅是乙方的實現代價,還有甲方的商用代價。設想一下,如果乙方將這樣的系統做出來了,那麼其售價會是多少?甲方爸爸雖然對乙方比較豪橫,但是其花起錢來,也不能無限 “爽”,畢竟地主家也沒有餘糧啊! 

我們繼續將目光投到本文所說的故事。如果甲方要求乙方必須做連通性檢測(也就是甲方不願意接受自己保證 L2 連通性的約束),那麼乙方很可能會將原來的冷備方案,替換爲雙活方案,如圖 9 所示。 

圖9 NMS 雙活系統示意

爲什麼是雙活系統呢?因爲 NMS 天然有檢測連通性的功能,如果採用雙活方案,NMS2 也就天然可以檢測 L2 的連通性。看起來很美好,好像原來的問題根本就不是問題,雙方的撕逼也根本就不應該發生。

但是,一切的歲月靜好,總是有人默默地負重前行。筆者只強調一點:圖 9 中標紅加粗的 L3,也即 NMS1 與 NMS2 之間的鏈接。爲了支撐雙活,L3 的帶寬、時延、丟包、抖動等一系列的網絡指標,都需要甲方保證。

再加上其他代價,甲方未必能夠承受。而且,雙活系統不是僅有好處,沒有壞處,甲方就算能夠承受初始代價,它仍然要承擔後續各種各樣的風險 ......

或者不採用雙活系統,而是採用其他方案,這背後的原理是一樣的:任何一種方案都不是絕對的優秀,都有其代價和風險。

數字出架構

對於每一種方案,都需要嚴格給出目標(收益)、風險、代價以後,才能做出最終的決策。這是另外一個話題了,本文就不再延展。

3.4 小結

其實,關於軟件架構設計,筆者的心得是二十個字,而不是十五個字。

範圍與目標

假設與約束

風險與代價

原則與規範

只是,前十五個字,更多的是面向黑盒、面向客戶、面向合約(“假設”,面向的是內部),後五個字,主要是面向白盒、面向內部、面向架構管理。所以,本文就只講述了前十五個字。

大道至簡,相由心生。當年聚賢莊一役,蕭鋒一套太祖長拳,令天下英雄概莫歎服!軟件技術發展日新月異,各種名詞、概念,甚至包括各種三方件、平臺、框架,你方唱罷我方登場,亂花漸欲迷人眼。越是如此,我們越是要撥雲見日,去追求軟件架構設計的至簡之大道!

由於時間關係,本文匆匆而就,有些話題也無法深入,敬請諒解。希望能早日抽出身來,寫一本軟件架構的祕笈,與君宮享!


(5 月底上市)

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