09.02 精讀《從零開始做架構》

架構定義

什麼是架構

一直以來,在軟件行業,對於什麼是架構,都有很多的爭論,每個人都有自己的理解。為了準確的理解架構,我們將和架構有關係而且相似的幾個概念進行梳理:

系統(system):泛指由一群有關聯的個體組成,根據某種規則運作,能完成個別元件不能單獨完成的工作的群體。系統分為自然系統與人為系統兩大類。

模塊(Module):是一套一致而互相有緊密關連的軟件組織。它分別包含了程序和數據結構兩部分。

組件(Component):自包含的、可編程的、可重用的、與語言無關的軟件單元,軟件組件可以很容易被用於組裝應用程序中。

軟件框架(Software Framework),通常指的是為了實現某個業界標準或完成特定基本任務的軟件組件規範,也指為了實現某個軟件組件規範時,提供規範所要求之基礎功能的軟件產品。

維基百科對於軟件架構的定義:軟件架構( Software Architecture)是有關軟件整體結構與組件的抽象描述,用於指導大型軟件系統各個方面的設計。

我比較認可的軟件架構定義如下:軟件架構定義了軟件有哪些組件,模塊,框架組成,以及他們是怎麼交互了連通,體現了軟件的其他非功能特性,例如擴展性,可維護性。

架構目的

從這個定義上看,也不是很清晰。為了講清楚這個問題,我們先來看看為什麼會產生架構。從軟件開發進化的歷史,探索一下軟件架構出現的歷史背景。

機器語言(1940 年之前):直接使用二進制碼 0 和 1 來表示機器可以識別的指令和數據,主要問題是三難:太難寫、太難讀、太難改!

彙編語言(20 世紀 40 年代):為了解決機器語言編寫、閱讀、修改複雜的問題,彙編語言應運而生。但是本身編寫複雜,同時還要考慮不同 CPU 的彙編指令和結構是不同的。

高級語言(20 世紀 50 年代):這些語言讓程序員不需要關注機器底層的低級結構和邏輯,而只要關注具體的問題和業務即可。

第一次軟件危機與結構化程序設計(20 世紀 60 年代~20 世紀 70 年代) :高級語言的出現,解放了程序員,但好景不長,隨著軟件的規模和複雜度的大大增加,20 世紀 60 年代中期開始爆發了第一次軟件危機,典型表現有軟件質量低下、項目無法如期完成、項目嚴重超支等,因為軟件而導致的重大事故時有發生。為了解決問題,“結構化程序設計”被提了出來。結構化程序設計本質上還是一種面向過程的設計思想,但通過“自頂向下、逐步細化、模塊化”的方法,將軟件的複雜度控制在一定範圍內,從而從整體上降低了軟件開發的複雜度。

第二次軟件危機與面向對象:第二次軟件危機主要體現在軟件的“擴展”變得非常複雜。結構化程序設計雖然能夠解決(也許用“緩解”更合適)軟件邏輯的複雜性,但是對於業務變化帶來的軟件擴展卻無能為力,軟件領域迫切希望找到新的銀彈來解決軟件危機,在這種背景下,面向對象的思想開始流行起來。雖然面向對象開始也被當作解決軟件危機的銀彈,但事實證明,和軟件工程一樣,面向對象也不是銀彈,而只是一種新的軟件方法而已。

軟件架構:軟件架構的出現有其歷史必然性。到了 20 世紀 90 年代“軟件架構”開始流行,創造了“組件”概念。我們可以看到,“模塊”“對象”“組件”本質上都是對達到一定規模的軟件進行拆分,差別只是在於隨著軟件的複雜度不斷增加,拆分的粒度越來越粗,拆分的層次越來越高。

架構設計的目的:為了解決複雜度帶來的問題

架構複雜度來源

高性能

軟件系統中高性能帶來的複雜度主要體現在兩方面,一方面是單臺計算機內部為了高性能帶來的複雜度;另一方面是多臺計算機集群為了高性能帶來的複雜度。

  1. 提高單機性。針對單臺計算機,通過升級軟、硬件能力實現性能提升。例如增大內存減少I/O操作(更換為固態硬盤(SSD)提升I/O訪問速度、使用RAID增加I/O吞吐能力)。能常見的技術點:多進程,多線程,進程間通信,併發。常見的服務器體系結構,SMP (Symmetric Multiprocessing) , 對稱多處理器、NUMA ( Non-Uniform Memory Access),非均勻訪問存儲模型、MPP (Massively ParallelProcessing),海量並行處理架構。
  2. 提高集群性能。利用合理的任務分配與任務分解實現性能的提升.常見的技術點:分配算法,負載均衡,系統拆分。

高可用

高可用是指系統無中斷地執行其功能的能力,代表系統的可用性程度,本質是通過“冗餘”來實現。常見高可用類型包括計算高可用,存儲高可用。

可擴展性

可擴展性指系統為了應對將來需求變化而提供的一種擴展能力,當有新的需求出現時,系統不需要或者僅需要少量修改就可以支持,無須整個系統重構或者重建。由於軟件系統固有的多變性,新的需求總會不斷提出來,因此可擴展性顯得尤其重要。對架構師要求:

  1. 預測變化:如何把握預測的準確性比較複雜,沒有固定的標準,靠直覺和經驗。
  2. 應對變化:技術上做到開閉原則,如何對擴展開放付對修改關閉,抽象出穩定和變化。

低成本

低成本可以是人力成本,服務器成本。低成本給架構設計帶來的主要複雜度體現在,往往只有“創新”才能達到低成本目標。這裡的“創新”既包括開創一個全新的技術領域(這個要求對絕大部分公司太高),也包括引入新技術,如果沒有找到能夠解決自己問題的新技術,那麼就真的需要自己創造新技術了。

安全

安全本身是一個龐大而又複雜的技術領域,並且一旦出問題,對業務和企業形象影響非常大。

從技術的角度來講,安全可以分為兩類:

一類是功能上的安全;功能安全其實就是“防小偷”,本質上是因為系統實現有漏洞。常見的安全問題,例如:常見的XSS攻擊、CSRF攻擊、SQL注入等。

另一類是架構上的安全。如果說“功能安全”是為了防小偷,那麼架構安全就是為了防強盜。

規模

規模帶來的複雜度主要原因在於“量變引起質變”,當數量超過一定閾值後,複雜度會發生質的變化。常見的規模帶來的複雜度有:

  1. 功能越來越多,導致系統的複雜度指數越來越高;
  2. 數據越來越多,系統的複雜度發生質變;

架構原則

合適原則

合適原則,合適優於業界領先。真正優秀的架構都是在企業當前人力、條件、業務等各種約束下設計出來的,能夠合理地將資源整合在一起併發揮出最大功效,並且能夠快速落地,不要面向簡歷去設計架構,高大上的架構不等於適用。

簡單原則

簡單原則,簡單優於複雜。面對系統結構、業務邏輯和複雜性,我們可以編寫出複雜的系統,但在軟件領域,複雜代表的是“問題”。架構設計時如果簡單的方案和複雜的方案都可以滿足需求,最好選擇簡單的方案。其實,簡單比複雜更加困難。

演化原則

演化原則,演化優於一步到位。業務在發展、技術在創新、外部環境在變化,這一切都是在告誡架構師不要貪大求全,或者盲目照搬大公司的做法。應該認真分析當前業務的特點,明確業務面臨的主要問題,設計合理的架構,快速落地以滿足業務需要,然後在運行過程中不斷完善架構,不斷隨著業務演化架構。

架構設計流程

識別複雜度

識別複雜度主要的複雜度問題列出來,然後根據業務、技術、團隊等綜合情況進行排序,優先解決當前面臨的最主要的複雜度問題。

(1)構建複雜度的來源清單——高性能、可用性、擴展性、安全、低成本、規模等。

(2)結合需求、技術、團隊、資源等對上述複雜度逐一分析是否需要?是否關鍵? “高性能”主要從軟件系統未來的TPS、響應時間、服務器資源利用率等客觀指標,也可以從用戶的主觀感受方面去考慮。 “可用性”主要從服務不中斷等質量方面去考慮。 “擴展性”則主要從功能需求的未來變更幅度等方面去考慮。

(3)按照上述的分析結論,得到複雜度按照優先級的排序清單,越是排在前面的複雜度,就越關鍵,就越優先解決。

設計備選方案

確定了系統面臨的主要複雜度問題後,方案設計就有了明確的目標,我們就可以開始真正進行架構方案設計了

架構設計備選方案的工作更多的是從需求、團隊、技術、資源等綜合情況出發,對主流、成熟的架構模式進行選擇、組合、調整、創新。

(1)根據自己的經驗設計一個方案。因為可選的模式有很多,組合的方案更多,往往一個問題的解決方案有很多個;如果再在組合的方案上進行一些創新,解決方案會更多。簡單原則,適合原則。

(2)看看業界優秀的方案是什麼,和我們業務場景比較,比較關鍵複雜度問題

(3)設計多個備選方案,3-5個左右,備選方案有差異,

評估和選擇備選方案

列出我們需要關注的質量屬性點,然後分別從這些質量屬性的維度去評估每個方案,再綜合挑選適合當時情況的最優方案。質量屬性按照優先級排序,首先挑選滿足第一優先級的,如果方案都滿足,那就再看第二優先級……以此類推。不要糾結,當差不多的時候,先落地比爭論搖擺要強

詳細方案設計

詳細方案設計就是將方案涉及的關鍵技術細節給確定下來。

架構模式實戰

高性能架構模式

存儲高性能

關係型數據庫。單個數據庫無法滿足業務需要,必須考慮數據庫集群的方式。高性能數據庫集群的第一種方式“讀寫分離”,將訪問的壓力分散到集群的多個節點上去。第二種方式“分庫分表”分散訪問壓力,分散存儲壓力。常見的開源框架:淘寶TDDL,360的Atlas。

高性能NOSQL:常見的NoSQL 方案有如下4 類:

  • K-V 存儲:解決關係數據庫無法存儲數據結構的問題,以Redis 為代表。
  • 文檔數據庫:解決關係數據庫強schema 約束的問題,以MongoDB 為代表。
  • 列式數據庫: 解決關係數據庫大數據場景下的I/O 問題,以HBase 為代表。
  • 全文搜索引擎:解決關係數據庫的全文搜索性能問題,以Elasticsearch 為代表。

緩存:緩存就是為了彌補存儲系統在這些複雜業務場景下的不足,其基本原理是將可能重複使用的數據放到內存中,一次生成、多次使用,避免每次使用都去訪問存儲系統。緩存常見問題有緩存雪崩、緩存穿透、緩存預熱、緩存熱點等。

  • 緩存雪崩是指當緩存失效(過期)後引起系統性能急劇下降的情況。
  • 緩存穿透是指緩存沒有發揮作用,業務系統雖然去 查緩存,但沒有命中,就需要再次去存儲系統查詢數據。
  • 緩存預熱(warm up)指系統上線後,將相關的緩存數據直接加載到緩存系統,而不是等待用戶訪問才來觸發緩存加載。
  • 緩存熱點:對於一些特別熱點的數據,如果大部分甚至所有的業務請求都命中同一份緩存數據,則這份數據所在的緩存服務器的壓力也很大

計算高性能

計算高性能主要集中在兩方面:

(1 )儘量提升單服務器的性能,將單服務器的性能發揮到極致。

(2 )如果單服務器無法支撐性能,設計服務器集群方案。

單機服務器高性能。單機高性能關鍵之一網絡編程模型涉及到兩個關鍵點:I/O 模型和進程模型

  • I/0 模型:阻塞、非阻塞、同步、異步。
  • 進程模型:單進程、多進程、多線程 PPC,TPC,Reactor,Proactor

集群高性能。高性能集群的本質很簡單,通過增加更多的服務器來提升系統整體的計算能力。高性能集群的複雜性主要體現在負載均衡和分配算法。

  1. 負載均衡:常用的負載均衡系統包括3種:DNS負載均衡、硬件負載均衡和軟件負載均衡。DNS負載均衡實現簡單、成本低,但也存在粒度太粗、負載均衡算法少等缺點。軟件和硬件的最主要區別就在於性能,硬件負載均衡性能遠遠高於軟件負載均衡性能。Nginx的性能是萬級,一般的Linux服務器上裝一個Nginx大概能到5萬/秒;LVS的性能是十萬級,據說可達到80萬/秒;而F5性能是百萬級,從200萬/秒到800萬/秒都有。

上述三種負載均衡的分類架構,各有自己的所長,其實可以遵循一個組合原則來使用它們。

  1. 負載均衡常見算法:

根據算法期望達到的目的,大體可以分為這麼幾類:

(1)任務平分類:負載均衡系統將接收到的任務平均分配給服務器進行處理

(2)負載均衡類:負載均衡系統根據服務器的負載來進行分配,

(3)性能最優類:負載均衡系統根據服務器的響應時間來進行任務分配

(4)Hash類:負載均衡系統根據任務中的某些關鍵信息進行Hash運算

高可用架構模式

高可用架構理論

CAP理論:在一個分佈式系統(指互相連接並共享數據的節點的集合)中,當涉及讀寫操作時,只能保證一致性(Consistence)、可用性(Availability)、 分區容錯性(Partition Tolerance)三者中的兩個,另外一個必須被犧牲。

可以注意上述描述中著重強調了兩點:互相連接和共享數據。因為分佈式系統並不一定會互聯和共享數據,只有滿足這兩點的系統才符合CAP理論。CAP原理是忽略網絡延遲的。

BASE是Basically Available(基本可用)、SofState(軟狀態)和Eventually Consistency(最終一致性)三個短語的縮寫,其核心思想是即使無法做到強一致性(CAP的一致性就是強一致性),但應用可以採用適合的方式達到最終一致性(Eventual Consistency)。

當談到數據一致性時,CAP、ACID、BASE難免都會被拿出來進行討論的,原因在於這三者都是和數據一致性相關的理論。ACID,數據庫事務的特性。CAP,分佈式系統設計理論。BASE,也是分佈式系統設計理論,延伸了CAP理論中AP方案。

FEMA分析方法

FMEA(Failure mode and effects analysis,故障模式與影響分析)又稱為失效模式與後果分析

FMEA 是一種在各行各業都有廣泛應用的可用性分析方法,通過對系統範圍內潛在的故障模式加以分析,並按照嚴重程度進行分類,以確定失效對於系統的最終影響。

回到軟件架構設計領域,FMEA 並不能指導我們如何做架構設計,而是當我們設計出一個架構後,再使用 FMEA 對這個架構進行分析,看看架構是否還存在某些可用性的隱患。

在架構設計領域,FMEA的具體分析方法如下:

  1. 給出初始的架構圖
  2. 假設架構中某個部件發生故障
  3. 分析此故障對系統功能的影響
  4. 根據分析結果,判斷架構是否需要優化。

存儲高可用

存儲高可用的本質是通過將數據複製到多和存儲設備,通過數據的冗餘方式來實現高可用。常見高可用存儲架構:主備(備庫只是一個備份),主從(主機負責讀寫,備庫可以用來讀),主主(都可以負責讀寫,保障數據能夠被雙向複製),數據分散集群(急群眾的每臺服務器會負責存儲一部分數據,同時也會備份一份數據),數據按照地理級別分區主備架構一些複雜點:主備狀態判斷(常見直連方式,中介方式,客戶端判斷),數據一致性。

在數據一致性中,常見技術有分佈式事務例如 兩階段提交,三階段提交;一致性算法例如Paxos,Raft,ZAB。

計算高可用

計算高可用的主要設計目標是當出現部分硬件損壞時,計算任務能夠繼續正常運行。因此計算高可用的本質是通過冗餘來規避部分故障的風險,單臺服務器是無論如何都達不到這個目標的。所以計算高可用的設計思想很簡單:通過增加更多服務器來達到計算高可用。計算高可用架構的設計複雜度主要體現在任務管理方面,即當任務在某臺服務器上執行失敗後,如何將任務重新分配到新的服務器進行執行。計算高可用架構:主備,計算集群。

業務高可用

從“異地多活”和“接口級別故障”兩個業務場景中,考慮如何保障業務高可用。

異地多活是指,正常情況下用戶無論訪問哪一個地點的業務系統,都能夠得到正確的業務服務;若某個地方業務異常的時候,用戶訪問其他不同地理位置正常的業務系統,也能夠得到正確的業務服務。

異地多活架構設計是為了應對在一些極端場景下,服務器出現故障如機房斷電、地震等等。但是,要實現異地多活的代價比較高,一方面系統的複雜度會變變高;另一方面,系統實現的成本也會變高。

設計技巧:異地多活設計技巧:保證核心業務的異地多活、保證核心數據最終一致性、採用多種手段同步數據、只保證絕大部分用戶的異地多活。

異地多活方案主要針對系統級別的故障,而接口級別故障,顧名思義就是針對接口級別的故障。其典型表現就是,系統沒有宕機、網絡沒有中斷,但是業務出現問題。內部可能是程序bug問題,外部可能是黑客攻擊、大量請求訪問等等。解決辦法一般分為三種:降級、熔斷、限流、排隊。

可擴展架構模式

軟件系統與硬件和建築系統最大的差異在於軟件是可擴展的,一個硬件生產出來後就不會再進行改變。而我們需要不斷地讓軟件系統具備更多的功能和特性,滿足新的需求或者順應技術發展的趨勢。

如何避免擴展時改動範圍太大,是軟件架構可擴展性設計的主要思考點。可擴展架構的基本思想就是:“拆”。就是將原本大一統的系統拆分成多個規模小的部分,擴展時只修改其中一部分即可,無須整個系統到處都改。

可以減少改動範圍,降低改動風險。3種拆分思路,面向流程拆分、面向服務拆分、面向功能拆分。

1.面向流程拆分:將整個業務流程拆分為幾個階段,每個階段作為一部分。面向流程拆分:展示層--》業務層--》數據層--》存儲層

精讀《從零開始做架構》

2.面向服務拆分:將系統提供的服務拆分,每個服務作為一部分。面向服務拆分:註冊服務、登錄服務、信息管理服務、安全設置服務。

精讀《從零開始做架構》

3.面向功能拆分:將系統提供的功能拆分,每個功能作為一部分。面向功能拆分:手機號註冊、郵箱註冊、手機號登錄、郵箱登錄、課程信息管理、成績信息管理、修改密碼、找回密碼。

精讀《從零開始做架構》

面向流程拆分:分層架構

分層架構是很常見的架構模式,它也叫 N 層架構,通常情況下,N 至少是 2 層。例如,C/S 架構、B/S 架構。常見的是 3 層架構(例如,MVC、MVP 架構)、4 層架構,5 層架構的比較少見,一般是比較複雜的系統才會達到或者超過 5 層,比如操作系統內核架構。

無論採取何種分層維度,分層架構設計最核心的一點就是需要保證各層之間的差異足夠清晰,邊界足夠明顯,讓人看到架構圖後就能看懂整個架構,這也是分層不能分太多層的原因。

分層架構另外一個典型的缺點就是性能,因為每一次業務請求都需要穿越所有的架構分層。

面向服務拆分:SOA、微服務

SOA 的全稱是 Service Oriented Architecture,中文翻譯為“面向服務的架構”。為了應對傳統 IT 系統存在的問題,SOA 提出了 3 個關鍵概念。ESB:ESB 的全稱是 Enterprise Service Bus。松耦合:松耦合的目的是減少各個服務間的依賴和互相影響。服務:所有業務功能都是一項服務。

微服務架構(Microservice Architecture)是一種架構概念,微服務架構是一種將單應用程序作為一套小型服務開發的方法,每種應用程序都在其自己的進程中運行,並與輕量級機制(通常是HTTP資源的API)進行通信。這些服務是圍繞業務功能構建的,可以通過全自動部署機制進行獨立部署。這些服務的集中化管理已經是最少的,它們可以用不同的編程語言編寫,並使用不同的數據存儲技術。

SOA 和微服務本質上是兩種不同的架構設計理念,只是在“服務”這個點上有交集而已。

精讀《從零開始做架構》

精讀《從零開始做架構》

微服務具體有哪些坑:

  • 服務劃分過細,服務間關係複雜
  • 服務數量太多,團隊效率急劇下降
  • 調用鏈太長,性能下降
  • 調用鏈太長,問題定位困難
  • 沒有自動化支撐,無法快速交付
  • 沒有服務治理,微服務數量多了後管理混亂

微服務架構最佳實踐:

服務粒度:“三個火槍手”原則,即一個微服務三個人負責開發。“三個火槍手”的原則主要應用於微服務設計和開發階段,如果微服務經過一段時間發展後已經比較穩定,處於維護期了,無須太多的開發,那麼平均 1 個人維護 1 個微服務甚至幾個微服務都可以。當然考慮到人員備份問題,每個微服務最好都安排 2 個人維護,每個人都可以維護多個微服務。

拆分方法:基於業務邏輯拆分,基於可靠性拆分,基於性能拆分,基於可擴展拆分(將系統中的業務模塊按照穩定性排序,將已經成熟和改動不大的服務拆分為穩定服務,將經常變化和迭代的服務拆分為變動服務)

面向功能拆分:微內核架構

微內核架構(Microkernel architecture)模式也被稱為插件架構(plugin architecture)模式。可以用來實現基於產品的應用, 比如Eclipse,在微內核的基礎上添加一些插件,就可以提供不同的產品,如C++, Java等。

微內核包含兩個組件: core system 和 plug-in modules。應用邏輯被分隔成核心繫統和插件模塊,可以提供可擴展的,靈活的,特性隔離的功能。

精讀《從零開始做架構》

微內核的架構本質就是將變化部分封裝在插件裡面,從而達到快速靈活擴展的目的,而又不影響整體系統的穩定。

微內核的核心繫統設計的關鍵技術有:插件管理(有哪些插件,怎麼加載,什麼時候加載)、插件連接(插件怎麼連接到核心系統)和插件通信(插件之間的通信)。相關框架技術:OSGi 的全稱是 Open Services Gateway initiative。

軟件可擴展性方法總結:

  1. 思想:拆分。開放/關閉原則:軟件實體應該對擴展開放,但對修改關閉。單一職責原則:一個類應該只有一個職責依賴倒置原則:依賴抽象
  2. 方法:面向對象方法、拆分方法
  3. 實戰模式:設計模式、架構模式、技術框架

互聯網標準技術架構圖

架構圖如下圖所示。這張圖基本涵蓋了互聯網技術公司的大部分技術點,不同公司只是在具體的技術實現上稍有差異,但不會跳出這個框架的範疇。

精讀《從零開始做架構》


分享到:


相關文章: