做好設計:架構模式
在 “軟件設計要素初探”[1] 一文,嘗試從軟件設計的整體角度,綜合討論了軟件設計的各種要素。本文探討確定系統整體結構的架構模式。
概述
「架構模式是系統組件及組件交互的模式,決定了處理數據和領域對象的全局控制結構。」
架構模式包含三個要素:
- 劃分方式:是在技術層面劃分,還是在領域層面劃分。
- 核心組件:包含哪些核心組件,組件之間如何交互。
- 設計重點:每一種架構模式都有其核心概念和設計重點。
架構模式評價
架構模式評價是指了解各種架構風格的優勢和劣勢,從而為業務選擇適合的架構。
- 簡單性:理解、構建和維護系統的簡單性。越簡單越好。
- 模塊化:模塊內內聚的程度(或模塊之間的耦合程度)。核心架構概念。《Fundamentals of Software Architecture》第三章專門探討了模塊化。后續單獨寫一篇文章。
- 性能: 架構支持緩存、異步、并發、批量等性能手段的能力。
- 高可用:故障恢復時長,不可用時間、不停機服務。出現局部故障時能否不影響服務可用性或者減少影響范圍。不可用時長越少越好,影響范圍越小越好。這很考驗互聯網企業的技術水平。最近兩年互聯網企業發生的幾起大規模不可用事件,也能給予人很好的啟發。支持高可用的技術主要有復制、冗余、負載均衡、異地多活等。
- 可靠性:考慮各組件可靠性及網絡拓撲對整體系統可靠性的影響。如果任一組件失效都會導致系統失效,則可靠性為組件可靠性之積;如果所有組件失效才會導致系統失效,則可靠性為組件可靠性的最低值。此外,網絡流量大的系統要達到高可靠性需要付出更多的努力。
- 可擴展:擴展新功能且對現有系統不影響的改動成本。越少越好。
- 可伸縮:通過水平方式擴展系統能力(性能和容量)。越簡單越好。
- 容錯:錯誤發生后影響系統服務的程度。越少越好。
- 易測性:mock,單一模塊測試,自動化測試的難易程度。越容易越好。
- 易部署性:整體系統部署成本及部署風險;改動后的部署成本和部署風險等。
架構模式清單
分層模式
圖片
分層架構是一種基于職責的單體架構。體現了康威定律:組織結構決定系統設計和開發。當還沒有確定架構模式時,分層模式是一個好的起點。網絡棧協議是分層模式的典型應用。
將應用劃分為多層,定義各層的接口、職責,以及各層之間的通信與交互。業務系統通常會劃分為表現層、業務邏輯層、持久層、數據層。業務邏輯層還可以分離出領域層。應用分層模式時,為確保系統可維護性,通常第j+1層只依賴于第j層的接口和服務。在性能場合下,可能會有跨層依賴的情況;應對特殊場景的設計中,會有“第j層的服務依賴于第j+1層的服務”的反向依賴關系。但這是個反模式,最好不要這么做。
分層體現了關注點分離原則。設計重點在于層的開閉與隔離(是否允許跨層訪問)。開閉是指某一層的上一層能否直接繞過本層訪問其下一層。開是能夠,閉是不能。層的閉可以起到隔離作用,即層的下一層改動不會影響到層的上一層,從而起到解耦的作用。如果跨層訪問非常多,可能意味著分層模式不太適合。
分層模式的最大優點是簡單易用。定義好各層的職責和邊界,就可以愉快地編碼了。
設計要素:
- 劃分方式:Technical-partitioned。
- 核心組件:Layer
- 設計重點:層的開閉。
- 優點:單體應用;簡單,容易理解與構建。
- 缺點:規模擴大后簡單性優點被削弱;少量代碼改動就需要整體重新編譯署和回歸測試,部署風險較高,可測試性低,可伸縮性低,容錯性低;需要手動添加負載均衡、多線程、緩存等方式來提升性能和高可用。
Pipeline模式
圖片
管道-過濾器鏈是一種基于流程的可擴展的單體架構。一個請求沿著管道連接的鏈路,依次由鏈上的過濾器進行處理。過濾器可以有多種類型:數據轉換、數據過濾、數據收集、數據存儲、數據顯示等。
- Shell 基于管道的命令組合是 Pipeline 模式的范例。
- JavaWeb應用的Servlet架構模式是“過濾器-處理器鏈”的典型應用;
- Java stream 也是 Pipeline 模式的應用。
- 數據同步也適合采用 Pipeline 模式,
Pipeline 模式的最大優點是簡單性與可擴展性。只要在鏈路上添加新的過濾器,即可擴展鏈路的處理能力。
設計要素:
- 劃分方式:Technical-partitioned。
- 核心組件:Filter, Pipeline,Context。
- 設計重點:Filter接口交互。
- 優點:單體應用,細粒度的模塊化;簡單,容易理解、構建和部署;可擴展性良好(添加新的Filter即可);可伸縮性良好(可以針對Filter做水平擴展);可測試性良好(可針對Filter獨立測試)。
- 缺點:少量代碼改動就需要整體重新編譯部署,部署風險高,容錯性中(一個 Filter 失敗可能無影響,可能會導致整體流程失敗);需要手動添加負載均衡、多線程、緩存等方式來提升性能和高可用。
微內核模式
圖片
亦稱“插件模式”。是一種基于功能組合的可擴展可定制的單體架構設計。
微內核架構主要由兩種組件組成:
- 核心系統組件:提供最小可用功能集及插件的注冊、加載和管理;
- 插件:用于擴展和定制功能。插件通常是相互獨立的。插件可以是基于運行時或編譯時。運行時部署的插件的優點是靈活裝載與卸載,插件變化核心系統不需要重新編譯部署,缺點是不太好管理插件。編譯時部署的插件是容易管理,但插件有變化則要整體重新編譯部署。
核心系統組件和插件,既可以通過接口(同步)也可以通過消息中間件(異步)來交互。通過同步來通信的優點是可靠,能及時處理插件失敗,缺點是核心系統可能受某個插件運行的影響從而影響整體。通過異步來通信的優點在于解耦,缺點在于插件如果失敗的話,很難快速通知到核心組件。
微內核的最主要特點是可擴展性和可定制。通過插件實現功能的解耦。其設計重點是定義良好的插件接口及插件交互機制,開發者只要遵循規范編寫和調試具體的插件并融入到系統,即可為系統增加新功能和新特性。
使用微內核架構的系統通常是產品型系統,有 Eclipse, Emacs, Firefox, Chrome 等。
- 劃分方式:Technical-partitioned,domain-partitioned。
- 核心組件:Core-System, Plug-in Components
- 設計重點:最小功能集設計、插件加載與管理。
- 優點:單體應用,細粒度的模塊化;簡單,容易理解、構建和部署;定制性良好;運行時插件可動態裝載或卸載;可測試性良好(針對插件單獨測試);容錯性中等(某個插件失敗不影響整體,除非Core-System失敗)。
- 缺點:可伸縮性中;需要手動添加負載均衡、多線程、緩存等方式來提升性能和高可用。
MVC模式
MVC ,“模型-視圖-控制”, 經典的 WebUI 架構模式。控制器處理請求從而更新模型和返回視圖,模型更新驅動視圖更新,視圖請求控制器處理。
經典的 JavaMVC 框架有 Struts2, SpringMVC, 前端 MVC 框架有Extjs4。
設計要素:
- 劃分方式:Technical-partitioned。
- 核心組件:Model, Viewer, Controller。
- 設計重點:model, controller
- 優點:單體應用,細粒度的模塊化,簡單,容易理解、構建和部署;前后端分離。
- 缺點:可靠性較低,可伸縮性低。
微服務模式
圖片
微服務模式是一種基于領域服務的高度解耦的可擴展可伸縮的分布式架構。
將單體應用分解為多個具有明確領域定義的業務子域,將每個相對獨立的業務子域實現成單獨的微服務。微服務獨立管理各自子域的問題,采用不同的架構和方案來適配自身領域的問題,最終所有微服務集成起來完成整體應用功能。實現獨立自治和發展、模塊化、分工協作等。微服務解決的是基礎服務和數據層的復用問題。
- 領域與代碼復用。
- 同一數據源的統一訪問。
- 單獨擴容某個服務容易。
微服務適用于中大型互聯網應用。不過微服務也有一定的復雜性。微服務面臨的問題是服務治理。主要包括:限流/熔斷降級、配置管理、日志中心、監控預警、鏈路跟蹤、故障隔離、動態擴容、分流發布、全鏈路壓測、中間件支撐、團隊組織架構適配與管理。微服務意味著要搭建一整套成熟的技術體系。
微服務要面對的若干問題:
- 拆分粒度:容易拆得太小。需要迭代出好的粒度。
- 通信:讓某一個領域服務成為 Mediator 或 單獨構建一個 Mediator 服務。
- 跨服務事務:增大服務粒度,使事務變成單體的;rollback;undo 機制。
設計要素:
- 劃分方式:Domain-partitioned。
- 核心組件:Domain, Bounded-context
- 設計重點:拆分粒度、數據隔離、邊界劃分。
- 優點:分布式系統,細粒度的模塊化;可伸縮性、可擴展、高可用、容錯性佳。
- 缺點:較為復雜,實施成本較高,性能方面中等(有很多網絡調用);可靠性中等(占用網絡帶寬、網絡延遲)。
事件驅動模式
圖片
事件驅動是一種基于消息的可擴展的分布式架構。
在系統內定義一系列的組件、事件及監聽器,組件發生變化時觸發事件,通知相應的監聽器處理事件更新組件,進而觸發新的事件,如此循環直至手動終止系統或系統崩潰。GUI 應用是事件驅動模式的典型范例。事件驅動模式通過消息進行解耦。從事件驅動模式可以衍生出訂閱-消費模式。大型互聯網應用中幾乎都存在訂閱和消費業務表更新或業務消息推送的子系統。需要高穩定可用的消息中間件,并仔細評估消息延遲對用戶活動造成的影響。新品消息推送、商品消費訂閱、發貨提醒等,我們正處于一個“消息/通知的訂閱-推送-被消費”的移動互聯網時代里。
從事件驅動模式還可以衍生出 Actor 模式。基于事件驅動的分布式的、異步并發的、可伸縮的、有故障恢復能力的大型消息處理架構。一個簡單例子可參見:“混合使用ForkJoin+Actor+Future實現一千萬個不重復整數的排序(Scala示例)”[2]
設計要素:
- 劃分方式:Technical-partitioned。
- 核心組件:Events, Queues(Channels), Event-processors 。
- 設計重點:廣播機制、通知機制。
- 優點:單體或分布式系統,細粒度的模塊化;解耦良好、性能優(異步+并行處理)、可擴展性好(容易添加新事件處理器)、可伸縮性好(負載均衡)、容錯性好(異步響應、重試、補償、最終一致性)。
- 缺點:較為復雜、可測性低(異步,執行路徑不確定性、事件樹流圖的組合非常多)。
規則-工作流模式
將系統分析成一系列的工作流節點以及規則的解析匹配,使用規則引擎來控制和運行,通過添加規則及規則流,實現可擴展性和可配置性。規則-工作流模式實現了邏輯表達與執行的分離,計算組件的復用。優點在于可擴展、可推導、自解釋性,面向業務人員使用。可參閱使用規則引擎計算工資的簡單例子:“Java Drools5.1 規則流基礎【示例】”[3],“基于規則和規則引擎的系統”[4] 。
設計要素:
- 劃分方式:Domain-partitioned。
- 核心組件:Rule, Workflow。
- 設計重點:規則、規則引擎,工作流引擎。
- 優點:單體或分布式,細粒度的模塊化;容易理解和維護;可擴展性好;易于部署和使用。
- 缺點:可測性中等,獨立規則容易測試,但組合規則可能出現非預期結果;性能取決于引擎性能。
Restful 模式
適合資源構建與共享的規范的可伸縮的架構模式,適合于構建API接口。
- 全局規范一致的資源邏輯命名、尋址、返回碼定義,關注資源與數據而非行為或服務。
- 無狀態的聲明式的請求;對客戶端隱藏實現細節。
- 通用的數據返回格式(JSON),跨語言與平臺。
- 使用名詞而非動詞作為路徑名。
設計要素:
- 劃分方式:Domain-partitioned。
- 核心組件:Rule, Workflow。
- 設計重點:規則、規則引擎,工作流引擎。
- 優點:單體或分布式,細粒度的模塊化;容易理解和維護;可擴展性好;易于部署和使用。
- 缺點:可測性中等,獨立規則容易測試,但組合規則可能出現非預期結果;性能取決于引擎性能。
參考資料
- 《Fundamentals of Software Architecture》[5]
- 《架構之美》第5章:“Web:面向資源的架構”。
Reference
[1]“軟件設計要素初探”:https://www.cnblogs.com/lovesqcc/p/7572682.html
[2]“混合使用ForkJoin+Actor+Future實現一千萬個不重復整數的排序(Scala示例)”:http://www.cnblogs.com/lovesqcc/p/5540415.html
[3]“Java Drools5.1 規則流基礎【示例】”:http://www.cnblogs.com/lovesqcc/archive/2011/01/18/4037863.html
[4]“基于規則和規則引擎的系統”:http://www.cnblogs.com/lovesqcc/archive/2012/05/16/4037817.html
[5]《Fundamentals of Software Architecture》:https://book.douban.com/subject/35306892/