.NET Core實戰項目之CMS 第九章 設計篇 白話架構設計 前面兩篇文章給大家介紹了我們實戰的CMS系統的資料庫設計,源碼也已經上傳到伺服器上了。今天我們就好聊聊架構設計,在開始之前先給大家分享一下這幾天我一直在聽的《從零開始學架構》裡面關於架構設計的定義以及架構設計的三大原則,希望能對大 ...
.NET Core實戰項目之CMS 第九章 設計篇-白話架構設計
前面兩篇文章給大家介紹了我們實戰的CMS系統的資料庫設計,源碼也已經上傳到伺服器上了。今天我們就好聊聊架構設計,在開始之前先給大家分享一下這幾天我一直在聽的《從零開始學架構》裡面關於架構設計的定義以及架構設計的三大原則,希望能對大家有所啟發。有著這些基礎之後,我們再基於此搭建我們的項目框架吧!如果你在閱讀的過程中有任何的問題,歡迎大家在留言區進行留言,或者加入.NET Core實戰項目群637326624跟大伙一起交流經驗。
本文已收錄至《.NET Core實戰項目之CMS 第一章 入門篇-開篇及總體規劃》
作者:依樂祝
寫在前面
程式員的成長繞不開架構設計,有時架構設計就像鴻溝一樣擋在程式的晉升之路上,只要跨過去就可以海闊天空,但不少技術能力很強的程式員卻依然不能完全掌握架構設計,包括我自己在內,在實踐過程中經常把握不住重點、分不清主次;或者說沒有徹底掌握架構設計的原則,在設計上舉棋不定。本文是我在觀看了李運華老師從零開始學架構後的一些看法,文章最後會給出如何查看原作的方法。文章大部分內容也都是摘錄自李運華的文章,當然,中間穿插了很多自己的認識在裡面。目的就是給大家分享一下架構方面的知識,希望本篇的內容分享能對你有所啟發!
架構的定義
對於技術人員來說,“架構”是一個再常見不過的詞了。我們會對新員工培訓整個系統的架構,參加架構設計評審,學習業界開源系統(例如,MySQL、Hadoop)的架構,研究大公司的架構實現(例如,微信架構、淘寶架構)……雖然“架構”這個詞常見,但如果深究一下“架構”到底指什麼呢?相信大部分人也許並不一定能夠準確地回答。例如:
- 架構和框架是什麼關係?有什麼區別?
- Windows有架構,SQL Server 有架構,.NET Core也有架構,使用 .NET Core開發、SQL Server 存儲、跑在 Windows 上的業務系統也有架構,那麼我們應該關註哪個架構呢?
- 微信有架構,微信的登錄系統也有架構,微信的支付系統也有架構,當我們談微信架構時,到底是在談什麼架構?
要想準確地回答這幾個問題,關鍵在於梳理幾個有關係而又相似的概念,包括:系統與子系統、模塊與組件、框架與架構。
系統與子系統
我們先來看維基百科定義的“系統”。
系統泛指由一群有關聯的個體組成,根據某種規則運作,能完成個別元件不能單獨完成的工作的群體。它的意思是“總體”“整體”或“聯盟”。
關鍵內容提煉:
- 關聯:系統是由一群有關聯的個體組成的,沒有關聯的個體堆在一起不能成為一個系統。例如,把一個發動機和一臺 PC 放在一起不能稱之為一個系統,把發動機、底盤、輪胎、車架組合起來才能成為一臺汽車。
- 規則:系統內的個體需要按照指定的規則運作,而不是單個個體各自為政。規則規定了系統內個體分工和協作的方式。例如,汽車發動機負責產生動力,然後通過變速器和傳動軸,將動力輸出到車輪上,從而驅動汽車前進。
- 能力:系統能力與個體能力有本質的差別,系統能力不是個體能力之和,而是產生了新的能力。例如,汽車能夠載重前進,而發動機、變速器、傳動軸、車輪本身都不具備這樣的能力。
維基百科定義的“子系統”
子系統也是由一群有關聯的個體所組成的系統,多半會是更大系統中的一部分。
其實子系統的定義和系統定義是一樣的,只是觀察的角度有差異,一個系統可能是另外一個更大系統的子系統。
按照這個定義,系統和子系統比較容易理解。我們以微信為例來做一個分析。
- 微信本身是一個系統,包含聊天、登錄、支付、朋友圈等子系統。
- 朋友圈這個系統又包括動態、評論、點贊等子系統。
- 評論這個系統可能又包括防刷子系統、審核子系統、發佈子系統、存儲子系統。
- 評論審核子系統不再包含業務意義上的子系統,而是包括各個模塊或者組件,這些模塊或者組件本身也是另外一個維度上的系統。例如,MySQL、Redis 等是存儲系統,但不是業務子系統。
模塊與組件
模塊和組件兩個概念在實際工作中很容易混淆,我們經常能夠聽到類似這樣的說法:
- MySQL 模塊主要負責存儲數據,而 ElasticSearch模塊主要負責數據搜索。
- 我們有安全加密組件、有審核組件。
- App 的下載模塊使用了第三方的組件。
造成這種現象的主要原因是,模塊與組件的定義並不好理解,也不能很好地進行區分。
兩者在維基百科上的定義:
軟體模塊(Module)是一套一致而互相有緊密關連的軟體組織。它分別包含了程式和數據結構兩部分。現代軟體開發往往利用模塊作為合成的單位。模塊的介面表達了由該模塊提供的功能和調用它時所需的元素。模塊是可能分開被編寫的單位。這使它們可再用和允許人員同時協作、編寫及研究不同的模塊。
軟體組件定義為自包含的、可編程的、可重用的、與語言無關的軟體單元,軟體組件可以很容易被用於組裝應用程式中。
從邏輯的角度來拆分系統後,得到的單元就是“模塊”;從物理的角度來拆分系統後,得到的單元就是“組件”。劃分模塊的主要目的是職責分離;劃分組件的主要目的是單元復用。其實,“組件”的英文 component 也可翻譯成中文的“零件”一詞,“零件”更容易理解一些,“零件”是一個物理的概念,並且具備“獨立且可替換”的特點。
框架與架構
框架是和架構比較相似的概念,且兩者有較強的關聯關係,所以在實際工作中,這兩個概念有時我們容易分不清楚。
框架與架構的區別:
軟體框架(Software Framework)通常指的是為了實現某個業界標準或完成特定基本任務的軟體組件規範,也指為了實現某個軟體組件規範時,提供規範所要求之基礎功能的軟體產品。
關鍵部分提煉:
- 框架是組件規範:例如,MVC 就是一種最常見的開發規範
- 框架提供基礎功能的產品:例如,WebApi 是 MVC 的開發框架,除了滿足 MVC 的規範,.NET Core WebApi 提供了很多基礎功能來幫助我們實現功能,包括Http請求,過濾器([HttpGet])等很多基礎功能。
軟體架構指軟體系統的“基礎結構”,創造這些基礎結構的準則,以及對這些結構的描述。
單純從定義的角度來看,框架和架構的區別還是比較明顯的,框架關註的是“規範”,架構關註的是“結構”。框架的英文是 Framework[ˈfreɪmwɜ:rk],架構的英文是 Architecture[ˈɑ:rkɪtektʃə(r)]。EF 的英文文檔標題就是“Entity framework”。
重新定義架構
參考維基百科的定義,架構重新定義為:軟體架構指軟體系統的頂層結構。
首先,“系統是一群關聯個體組成”,這些“個體”可以是“子系統”“模塊”“組件”等;架構需要明確系統包含哪些“個體”。
其次,系統中的個體需要“根據某種規則”運作,架構需要明確個體運作和協作的規則。
第三,維基百科定義的架構用到了“基礎結構”這個說法,我改為“頂層結構”,可以更好地區分系統和子系統,避免將系統架構和子系統架構混淆在一起導致架構層次混亂。
總結提煉上述概念
- 架構是頂層設計;
- 框架是面向編程或配置的半成品;
- 組件是從技術維度上的復用;
- 模塊是從業務維度上職責的劃分;
- 系統是相互協同可運行的實體。
架構設計三原則
合適原則、簡單原則、演化原則,架構設計時遵循這幾個原則,有助於做出最好的選擇。
合適原則
合適原則宣言:“合適優於業界領先”。
優秀的技術人員都有很強的技術情結,當他們做方案或者架構時,總想不斷地挑戰自己,想達到甚至優於業界領先水平是其中一個典型表現,因為這樣才能夠展現自己的優秀,才能在年終 KPI 績效總結裡面驕傲地寫上“設計了 XX 方案,達到了和 Google 相同的技術水平”“XX 方案的性能測試結果大大優於阿裡集團的 YY 方案”。
但現實是,大部分這樣想和這樣做的架構,最後可能都以失敗告終!
為什麼會這樣呢?
再好的夢想,也需要腳踏實地實現!這裡的“腳踏實地”主要體現在下麵幾個方面。
- 將軍難打無兵之仗
大公司的分工比較細,一個小系統可能就是一個小組負責,比如說某個通信大廠,做一個 OM 管理系統就有十幾個人,阿裡的中間件團隊有幾十個人,而大部分公司,整個研發團隊可能就 100 多人,某個業務團隊可能就十幾個人。十幾個人的團隊,想做幾十個人的團隊的事情,而且還要做得更好,不能說絕對不可能,但難度是可想而知的。
沒那麼多人,卻想乾那麼多活,是失敗的第一個主要原因。
- 羅馬不是一天建成的
業界領先的很多方案,其實並不是一堆天才某個時期靈機一動,然後加班加點就做出來的,而是經過幾年時間的發展才逐步完善和初具規模的。阿裡中間件團隊 2008 年成立,發展到現在已經有十年了。我們只知道他們抗住了多少次“雙 11”,做了多少優秀的系統,但經歷了什麼樣的挑戰、踩了什麼樣的坑,只有他們自己知道!這些挑戰和踩坑,都是架構設計非常關鍵的促進因素,單純靠拍腦袋或者頭腦風暴,是不可能和真正實戰相比的。
沒有那麼多積累,卻想一步登天,是失敗的第二個主要原因。
- 冰山下麵才是關鍵
可能有人認為,業界領先的方案都是天才創造出來的,所以自己也要造一個業界領先的方案,以此來證明自己也是天才。確實有這樣的天才,但更多的時候,業界領先的方案其實都是“逼”出來的!簡單來說,“業務”發展到一定階段,量變導致了質變,出現了新的問題,已有的方式已經不能應對這些問題,需要用一種新的方案來解決,通過創新和嘗試,才有了業界領先的方案。GFS 為何在 Google 誕生,而不是在 Microsoft 誕生?我認為 Google 有那麼龐大的數據是一個主要的因素,而不是因為 Google 的工程師比 Microsoft 的工程師更加聰明。
沒有那麼卓越的業務場景,卻幻想靈光一閃成為天才,是失敗的第三個主要原因。
所以,真正優秀的架構都是在企業當前人力、條件、業務等各種約束下設計出來的,能夠合理地將資源整合在一起併發揮出最大功效,並且能夠快速落地。這也是很多 BAT 出來的架構師到了小公司或者創業團隊反而做不出成績的原因,因為沒有了大公司的平臺、資源、積累,只是生搬硬套大公司的做法,失敗的概率非常高。
簡單原則
簡單原則宣言:“簡單優於複雜”。
軟體架構設計是一門技術活。所謂技術活,從歷史上看,無論是瑞士的鐘錶,還是瓦特的蒸汽機;無論是萊特兄弟發明的飛機,還是摩托羅拉發明的手機,無一不是越來越精細、越來越複雜。因此當我們進行架構設計時,會自然而然地想把架構做精美、做複雜,這樣才能體現我們的技術實力,也才能夠將架構做成一件藝術品。
由於軟體架構和建築架構錶面上的相似性,我們也會潛意識地將對建築的審美觀點移植到軟體架構上面。我們驚嘆於長城的巨集偉、泰姬陵的精美、悉尼歌劇院的藝術感、迪拜帆船酒店的豪華感,因此,對於我們自己親手打造的軟體架構,我們也希望它巨集偉、精美、藝術、豪華……總之就是不能寒酸、不能簡單。
團隊的壓力有時也會有意無意地促進我們走向複雜的方向,因為大部分人在評價一個方案水平高低的時候,複雜性是其中一個重要的參考指標。例如設計一個主備方案,如果你用心跳來實現,可能大家都認為這太簡單了。但如果你引入 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 個小時重啟一次。
- 線上運行出現故障,幾十個人撲上去定位和處理,一間小黑屋都裝不下所有人,整個辦公區鬧翻天。
- ……
不用多說,肯定誰都無法忍受這樣的場景。
但是,為什麼複雜的電路就意味更強大的功能,而複雜的架構卻有很多問題呢?根本原因在於電路一旦設計好後進入生產,就不會再變,複雜性只是在設計時帶來影響;而一個軟體系統在投入使用後,後續還有源源不斷的需求要實現,因此要不斷地修改系統,複雜性在整個系統生命周期中都有很大影響。
功能複雜的組件,另外一個典型特征就是採用了複雜的演算法。複雜演算法導致的問題主要是難以理解,進而導致難以實現、難以修改,並且出了問題難以快速解決。
以 ZooKeeper 為例,ZooKeeper 本身的功能主要就是選舉,為了實現分散式下的選舉,採用了 ZAB 協議,所以 ZooKeeper 功能雖然相對簡單,但系統實現卻比較複雜。相比之下,etcd 就要簡單一些,因為 etcd 採用的是 Raft 演算法,相比 ZAB 協議,Raft 演算法更加容易理解,更加容易實現。
綜合前面的分析,我們可以看到,無論是結構的複雜性,還是邏輯的複雜性,都會存在各種問題,所以架構設計時如果簡單的方案和複雜的方案都可以滿足需求,最好選擇簡單的方案。《UNIX 編程藝術》總結的 KISS(Keep It Simple, Stupid!)原則一樣適應於架構設計。
演化原則
演化原則宣言:“演化優於一步到位”。
軟體架構從字面意思理解和建築結構非常類似,事實上“架構”這個詞就是建築領域的專業名詞,維基百科對“軟體架構”的定義中有一段話描述了這種相似性:
從和目的、主題、材料和結構的聯繫上來說,軟體架構可以和建築物的架構相比擬。
例如,軟體架構描述的是一個軟體系統的結構,包括各個模塊,以及這些模塊的關係;建築架構描述的是一幢建築的結構,包括各個部件,以及這些部件如何有機地組成成一幢完美的建築。
然而,字面意思上的相似性卻掩蓋了一個本質上的差異:建築一旦完成(甚至一旦開建)就不可再變,而軟體卻需要根據業務的發展不斷地變化!
- 古埃及的吉薩大金字塔,4000 多年前完成的,到現在還是當初的架構。
- 中國的明長城,600 多年前完成的,現在保存下來的長城還是當年的結構。
- 美國白宮,1800 年建成,200 年來進行了幾次擴展,但整體結構並無變化,只是在旁邊的空地擴建或者改造內部的佈局。
對於建築來說,永恆是主題;而對於軟體來說,變化才是主題。軟體架構需要根據業務的發展而不斷變化。設計 Windows 和 Android 的人都是頂尖的天才,即便如此,他們也不可能在 1985 年設計出 Windows 8,不可能在 2009 年設計出 Android 6.0。
如果沒有把握“軟體架構需要根據業務發展不斷變化”這個本質,在做架構設計的時候就很容易陷入一個誤區:試圖一步到位設計一個軟體架構,期望不管業務如何變化,架構都穩如磐石。
為了實現這樣的目標,要麼照搬業界大公司公開發表的方案;要麼投入龐大的資源和時間來做各種各樣的預測、分析、設計。無論哪種做法,後果都很明顯:投入巨大,落地遙遙無期。更讓人沮喪的是,就算跌跌撞撞拼死拼活終於落地,卻發現很多預測和分析都是不靠譜的。
考慮到軟體架構需要根據業務發展不斷變化這個本質特點,軟體架構設計其實更加類似於大自然“設計”一個生物,通過演化讓生物適應環境,逐步變得更加強大:
- 首先,生物要適應當時的環境。
- 其次,生物需要不斷地繁殖,將有利的基因傳遞下去,將不利的基因剔除或者修複。
- 第三,當環境變化時,生物要能夠快速改變以適應環境變化;如果生物無法調整就被自然淘汰;新的生物會保留一部分原來被淘汰生物的基因。
軟體架構設計同樣是類似的過程:
- 首先,設計出來的架構要滿足當時的業務需要。
- 其次,架構要不斷地在實際應用過程中迭代,保留優秀的設計,修複有缺陷的設計,改正錯誤的設計,去掉無用的設計,使得架構逐漸完善。
- 第三,當業務發生變化時,架構要擴展、重構,甚至重寫;代碼也許會重寫,但有價值的經驗、教訓、邏輯、設計等(類似生物體內的基因)卻可以在新架構中延續。
架構師在進行架構設計時需要牢記這個原則,時刻提醒自己不要貪大求全,或者盲目照搬大公司的做法。應該認真分析當前業務的特點,明確業務面臨的主要問題,設計合理的架構,快速落地以滿足業務需要,然後在運行過程中不斷完善架構,不斷隨著業務演化架構。
即使是大公司的團隊,在設計一個新系統的架構時,也需要遵循演化的原則,而不應該認為團隊人員多、資源多,不管什麼系統上來就要一步到位,因為業務的發展和變化是很快的,不管多牛的團隊,也不可能完美預測所有的業務發展和變化路徑。
本節總結
架構即決策。架構需要面向業務需求,併在各種資源(人、財、物、時、事)約束條件下去做權衡、取捨。而決策就會存在不確定性。採用一些高屋建瓴的設計原則有助於去消除不確定,去逼近解決問題的最優解。
1 合適原則
架構無優劣,但存合適性。“汝之蜜糖,吾之砒霜”;架構一定要匹配企業所在的業務階段;不要面向簡歷去設計架構,高大上的架構不等於適用;削足適履與打腫充胖都不符合合適原則;所謂合適,一定要匹配業務所處階段,能夠合理地將資源整合在一起併發揮出最大功效,並能夠快速落地。
2 簡單原則
"我沒有時間寫一封簡訊,所以只好寫一封長信"。其實,簡單比複雜更加困難。面對系統結構、業務邏輯和複雜性,我們可以編寫出複雜的系統,但在軟體領域,複雜代表的是“問題”。架構設計時如果簡單的方案和複雜的方案都可以滿足需求,最好選擇簡單的方案。但是,事實上,當軟體系統變得太複雜後,就會有人換一個思路進行重構、升級,將它重新變得簡單,這也是軟體開發的大趨勢。 簡單原則是一個朴素且偉大的原則,Google的MapReduce系統就採用了分而治之的思想,而背後就是將複雜問題轉化為簡單問題的典型案例。
3 演化原則
大到人類社會、自然生物,小到一個細胞,似乎都遵循這一普世原則,軟體架構也不例外。業務在發展、技術在創新、外部環境在變化,這一切都是在告誡架構師不要貪大求全,或者盲目照搬大公司的做法。應該認真分析當前業務的特點,明確業務面臨的主要問題,設計合理的架構,快速落地以滿足業務需要,然後在運行過程中不斷完善架構,不斷隨著業務演化架構。懷胎需要十月,早一月或晚一月都很危險。
總結
今天我帶著大家記錄一下李運華老師從零開始學架構的關於架構的概念以及架構的三個原則,希望對您有所啟發!如果你想學習更多關於架構方面的知識也可以訂閱李老師的課程,文章最後我會給出微信二維碼!好了,下篇我們就基於這些思想設計最適合我們實際的.NET Core CMS系統的開發框架吧!
這裡免費給李老師打個廣告,畢竟本篇文章摘錄了很多李老師的內容!
http://gk.link/a/101w9