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