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