導讀:《架構設計》系列為極客時間李運華老師《從0開始學架構》課程筆記。本文為第一部分,主要介紹架構設計的歷史背景、相關概念、目的、複雜度、原則以及流程。從整體上對架構設計有所瞭解。
架構設計的歷史背景
機器語言(1940 年之前)
機器語言的主要問題是三難:太難寫、太難讀、太難改!
組合語言(20 世紀 40 年代)
本質上還是面向機器的,因為寫組合語言需要我們精確瞭解計算機底層的知識。
高階語言(20 世紀 50 年代)
第一次軟體危機與結構化程式設計(20 世紀 60 年代~20 世紀 70 年代)
- 典型表現有軟體質量低下、專案無法如期完成、專案嚴重超支等,因為軟體而導致的重大事故時有發生。
- 軟體工程同樣無法根除軟體危機,只能在一定程度上緩解軟體危機。
- 結構化程式設計的主要特點是拋棄 goto 語句,採取“自頂向下、逐步細化、模組化”的指導思想。結構化程式設計本質上還是一種程式導向的設計思想,但通過“自頂向下、逐步細化、模組化”的方法,將軟體的複雜度控制在一定範圍內,從而從整體上降低了軟體開發的複雜度。
第二次軟體危機與物件導向(20 世紀 80 年代)
- 第二次軟體危機的根本原因還是在於軟體生產力遠遠跟不上硬體和業務的發展。第一次軟體危機的根源在於軟體的“邏輯”變得非常複雜,而第二次軟體危機主要體現在軟體的“擴充套件”變得非常複雜。
- 結構化程式設計雖然能夠解決(也許用“緩解”更合適)軟體邏輯的複雜性,但是對於業務變化帶來的軟體擴充套件卻無能為力,軟體領域迫切希望找到新的銀彈來解決軟體危機,在這種背景下,物件導向的思想開始流行起來。
軟體架構的歷史背景
隨著軟體系統規模的增加,計算相關的演算法和資料結構不再構成主要的設計問題;當系統由許多部分組成時,整個系統的組織,也就是所說的“軟體架構”,導致了一系列新的設計問題。
- 系統規模龐大,內部耦合嚴重,開發效率低;
- 系統耦合嚴重,牽一髮動全身,後續修改和擴充套件困難;
- 系統邏輯複雜,容易出問題,出問題後很難排查和修復
三組容易混淆的概念
系統與子系統
系統
- 系統泛指由一群有關聯的個體組成,根據某種規則運作,能完成個別元件不能單獨完成的工作的群體。它的意思是“總體”“整體”或“聯盟”
- 關係:系統是由一群有關聯的個體組成的,沒有關聯的個體堆在一起不能成為一個系統。
- 規則:系統內的個體需要按照指定的規則運作,而不是單個個體各自為政。規則規定了系統內個體分工和協作的方式。
- 能力:系統能力與個體能力有本質的差別,系統能力不是個體能力之和,而是產生了新的能力。
子系統
子系統也是由一群有關聯的個體所組成的系統,多半會是更大系統中的一部分。
總結
一個系統的架構,只包括頂層這一個層級的架構,而不包括下屬子系統層級的架構。所以微信架構,就是指微信系統這個層級的架構。當然,微信的子系統,比如支付系統,也有它自己的架構,同樣只包括頂層。
模組與元件
模組
軟體模組(Module)是一套一致而互相有緊密關聯的軟體組織。它分別包含了程式和資料結構兩部分。現代軟體開發往往利用模組作為合成的單位。模組的介面表達了由該模組提供的功能和呼叫它時所需的元素。模組是可能分開被編寫的單位。這使它們可再用和允許人員同時協作、編寫及研究不同的模組。
元件
軟體元件定義為自包含的、可程式設計的、可重用的、與語言無關的軟體單元,軟體元件可以很容易被用於組裝應用程式中。
總結
- 模組和元件都是系統的組成部分,只是從不同的角度拆分系統而已。
- 從業務邏輯的角度來拆分系統後,得到的單元就是“模組”;從物理部署的角度來拆分系統後,得到的單元就是“元件”。劃分模組的主要目的是職責分離;劃分元件的主要目的是單元複用。
- 假設我們要做一個學生資訊管理系統,這個系統從邏輯的角度來拆分,可以分為“登入註冊模組”“個人資訊模組”和“個人成績模組”;從物理的角度來拆分,可以拆分為 Nginx、Web 伺服器和 MySQL。
- 業務系統的架構師,首先需要思考怎麼從業務邏輯的角度把系統拆分成一個個模組角色,其次需要思考怎麼從物理部署的角度把系統拆分成元件角色,例如選擇 MySQL 作為儲存系統。但是對於 MySQL 內部的體系架構(是可以不用關注的,也不需要在你的業務系統架構中展現這些內容。
框架與架構
框架
軟體框架(Software framework)通常指的是為了實現某個業界標準或完成特定基本任務的軟體元件規範,也指為了實現某個軟體元件規範時,提供規範所要求之基礎功能的軟體產品。
架構
軟體架構指軟體系統的“基礎結構”,創造這些基礎結構的準則,以及對這些結構的描述。
總結
- 框架關注的是“規範”,架構關注的是“結構”。
- 框架是一整套開發規範,架構是某一套開發規範下的具體落地方案,包括各個模組之間的組合關係以及它們協同起來完成功能的運作規則。
重新定義架構:4R 架構
- 軟體架構指軟體系統的頂層(Rank)結構,它定義了系統由哪些角色(Role)組成,角色之間的關係(Relation)和運作規則(Rule)
- Rank:它是指軟體架構是分層的,對應“系統”和“子系統”的分層關係。通常情況下,我們只需要關注某一層的架構,最多展示相鄰兩層的架構,而不需要把每一層的架構全部糅雜在一起。無論是架構設計還是畫架構圖,都應該採取“自頂向下,逐步細化”的方式。
- Role:它是指軟體系統包含哪些角色,每個角色都會負責系統的一部分功能。架構設計最重要的工作之一就是將系統拆分為多個角色。最常見的微服務拆分其實就是將整體複雜的業務系統按照業務領域的方式,拆分為多個微服務,每個微服務就是系統的一個角色。
- Relation:它是指軟體系統的角色之間的關係,對應到架構圖中其實就是連線線,角色之間的關係不能亂連,任何關係最後都需要程式碼來實現,包括連線方式(HTTP、TCP、UDP 和串列埠等)、資料協議(JSON、XML 和二進位制等)以及具體的介面等。
- Rule:它是指軟體系統角色之間如何協作來完成系統功能。
架構設計的目的
架構也是為了應對軟體系統複雜度而提出的一個解決方案,即架構設計的主要目的是為了解決軟體系統複雜度帶來的問題。
- 遵循這條準則能夠讓“新手”架構師心中有數,而不是一頭霧水。
- 遵循這條準則能夠讓“老鳥”架構師有的放矢,而不是貪大求全。
複雜性來源
高效能
軟體系統中高效能帶來的複雜度主要體現在兩方面,一方面是單臺計算機內部為了高效能帶來的複雜度;另一方面是多臺計算機叢集為了高效能帶來的複雜度。
單機複雜度
- 作業系統和效能最相關的就是程式和執行緒
- 如果我們要完成一個高效能的軟體系統,需要考慮如多程式、多執行緒、程式間通訊、多執行緒併發等技術點,而且這些技術並不是最新的就是最好的,也不是非此即彼的選擇。
叢集複雜度
- 任務分配,負載均衡
任務分解,微服務
- 簡單的系統更加容易做到高效能
- 可以針對單個任務進行擴充套件
指標
衡量軟體效能包括了響應時間、TPS、伺服器資源利用率等客觀指標
高可用
系統無中斷地執行其功能的能力,代表系統的可用性程度,是進行系統設計時的準則之一。
系統的高可用方案五花八門,但萬變不離其宗,本質上都是通過“冗餘”來實現高可用。
- 高效能增加機器目的在於“擴充套件”處理效能;
- 高可用增加機器目的在於“冗餘”處理單元。
計算高可用
“計算”指的是業務的邏輯處理。計算有一個特點就是無論在哪臺機器上進行計算,同樣的演算法和輸入資料,產出的結果都是一樣的
儲存高可用
- 整個系統的高可用設計關鍵點和難點就在於“儲存高可用”。儲存與計算相比,有一個本質上的區別:將資料從一臺機器搬到到另一臺機器,需要經過線路進行傳輸。
- 儲存高可用的難點不在於如何備份資料,而在於如何減少或者規避資料不一致對業務造成的影響。
高可用狀態決策
無論是計算高可用還是儲存高可用,其基礎都是“狀態決策”,即系統需要能夠判斷當前的狀態是正常還是異常,如果出現了異常就要採取行動來保證高可用
- 獨裁式
- 協商式
- 民主式
可擴充套件性
可擴充套件性是指,系統為了應對將來需求變化而提供的一種擴充套件能力,當有新的需求出現時,系統不需要或者僅需要少量修改就可以支援,無需整個系統重構或者重建。
設計具備良好可擴充套件性的系統,有兩個基本條件:
正確預測變化
- 原則:只預測 2 年內的可能變化,不要試圖預測 5 年甚至 10 年後的變化。
完美應對變化
- 方案一:提煉出“變化層”和“穩定層”,核心思想是通過變化層來隔離變化
- 方案二:提煉出“抽象層”和“實現層”,核心思想就是通過實現層來封裝變化。
- 原則:1 寫 2 抄 3 重構原則
其他來源
成本
往往只有“創新”才能達到低成本目標
- 引進新技術
- 自研新技術
低成本本質上是與高效能和高可用衝突的,所以低成本很多時候不會是架構設計的首要目標,而是架構設計的附加約束。
安全
- 功能安全:其實就是“防小偷”
- 架構安全:就是“防強盜”
規模
- 規模帶來複雜度的主要原因就是“量變引起質變”
- 資料越來越多,系統複雜度發生質變
架構設計原則
合適原則,“合適優於業界領先”
不適合的情況
- 沒那麼多人,卻想幹那麼多活
- 沒有那麼多積累,卻想一步登天
- 沒有那麼卓越的業務場景,卻幻想靈光一閃成為天才
簡潔原則,“簡潔優於複雜”
複雜的表現
結構的複雜性
現象
- 組成複雜系統的元件數量更多
- 同時這些元件之間的關係也更加複雜
問題
- 元件越多,就越有可能其中某個元件出現故障
- 某個元件改動,會影響關聯的所有元件
- 定位一個複雜系統中的問題總是比簡單系統更加困難
邏輯的複雜性
- 單個元件承擔了太多的功能
- 採用了複雜的演算法
演進原則,“演化優於一步到位”
對於軟體來說,變化才是主題
軟體架構設計其實更加類似於大自然“設計”一個生物,通過演化讓生物適應環境,逐步變得更加強大
避坑
- 時刻提醒自己不要貪大求全
- 避免盲目照搬大公司的做法
最佳實踐
- 應該認真分析當前業務的特點,明確業務面臨的主要問題,設計合理的架構,快速落地以滿足業務需要
- 在執行過程中不斷完善架構,不斷隨著業務演化架構
架構設計流程
識別複雜度
- 將主要的複雜度問題列出來,然後根據業務、技術、團隊等綜合情況進行排序,優先解決當前面臨的最主要的複雜度問題。
- 設計的目標應該以峰值來計算。峰值一般取平均值的 3 倍,
- 設計目標設定為峰值的 4 倍是根據業務發展速度來預估的,不是固定為 4 倍,不同的業務可以是 2 倍,也可以是 8 倍,但一般不要設定在 10 倍以上,更不要一上來就按照 100 倍預估。
設計備選方案
新技術都是在現有技術的基礎上發展起來的。`
`
常見錯誤
- 設計最優秀的方案
只做一個方案
弊端
- 心裡評估過於簡單,可能沒有想得全面
- 單一方案設計會出現過度辯護的情況,
- 架構師再怎麼牛,經驗知識和技能也有侷限
實踐
- 備選方案的數量以 3 ~ 5 個為最佳
- 備選方案的差異要比較明顯
- 備選方案的技術不要只侷限於已經熟悉的技術
備選方案過於詳細
- 耗費了大量的時間和精力
- 將注意力集中到細節中,忽略了整體的技術設計,導致備選方案數量不夠或者差異不大。
- 評審的時候其他人會被很多細節給繞進去,評審效果很差。
- 正確的做法是備選階段關注的是技術選型,而不是技術細節,技術選型的差異要比較明顯
評估和選擇備選方案
常見指導思想
- 最簡派
- 最牛派
- 最熟派
- 領導派
評估策略
- 列出我們需要關注的質量屬性點,然後分別從這些質量屬性的維度去評估每個方案,再綜合挑選適合當時情況的最優方案
- 常見的方案質量屬性點有:效能、可用性、硬體成本、專案投入、複雜度、安全性、可擴充套件性等
- 通常情況下,如果某個質量屬性評估和業務發展有關係(例如,效能、硬體成本等),需要評估未來業務發展的規模時,一種簡單的方式是將當前的業務規模乘以 2 ~4 即可,如果現在的基數較低,可以乘以 4;如果現在基數較高,可以乘以 2。
- 即架構師綜合當前的業務發展情況、團隊人員規模和技能、業務發展預測等因素,將質量屬性按照優先順序排序,首先挑選滿足第一優先順序的,如果方案都滿足,那就再看第二優先順序……以此類推。
詳細設計方案
詳細設計方案階段可能遇到的一種極端情況就是在詳細設計階段發現備選方案不可行。
- 架構師不但要進行備選方案設計和選型,還需要對備選方案的關鍵細節有較深入的理解
- 通過分步驟、分階段、分系統等方式,儘量降低方案複雜度,方案本身的複雜度越高,某個細節推翻整個方案的可能性就越高,適當降低複雜性,可以減少這種風險。