來源:《持續(xù)交付2.0》,作者 喬梁
信息的快速變化是我們這個(gè)時(shí)代的特點(diǎn),誰能更快地捕捉和利用信息帶來的價(jià)值誰就能在競爭中占得先機(jī),軟件發(fā)布和部署的頻率也因此被要求能夠跟上外部業(yè)務(wù)變化的速度,這種頻率已經(jīng)從以往的若干個(gè)月、若干個(gè)周進(jìn)化到天、小時(shí)、分甚至是秒,這就好比一輛在高速公路上行駛的汽車,我們要一邊行駛還得一邊不停地更新車上的零部件。由此可見,為了能夠更好地應(yīng)對(duì)業(yè)務(wù)發(fā)展,持續(xù)交付是必然趨勢,這一目標(biāo)達(dá)成需要軟件架構(gòu)按照“大系統(tǒng)小做”的原則進(jìn)行設(shè)計(jì)和演進(jìn)。
“大系統(tǒng)小做”原則
所謂“大系統(tǒng)小做”原則是指架構(gòu)設(shè)計(jì)應(yīng)該要考慮:
- 為測試而設(shè)計(jì)(design for test),在我接觸過的諸多項(xiàng)目里,測試過程(研發(fā)自測、集成測試、方案測試)絕對(duì)是一個(gè)時(shí)間消耗大戶,如果解決不了這個(gè)問題,那么快速發(fā)布和部署就是空談。回想我們?nèi)粘5膱鼍按笾率沁@樣的:先花一些時(shí)間打包部署一個(gè)應(yīng)用,然后開始測試,測試過程中發(fā)現(xiàn)了一個(gè)bug,分析了半天發(fā)現(xiàn)是一個(gè)簡單的錯(cuò)誤。這還只是一個(gè)簡單的場景,如果涉及到多人、跨團(tuán)隊(duì)、跨職能部門,那情況復(fù)雜的程度還會(huì)翻倍。那么為何不在寫代碼時(shí)就把這樣的錯(cuò)誤給杜絕呢?很大原因就在于前期設(shè)計(jì)時(shí)埋下了隱患,沒有考慮“可測試性”??蓽y試性為什么如此重要?因?yàn)槲覀冏鲈O(shè)計(jì),其實(shí)就是把一個(gè)軟件拆分成一個(gè)一個(gè)的小模塊。如果不盡可能地保證每個(gè)小模塊的正確性,而只是從最外圍的系統(tǒng)角度去驗(yàn)證系統(tǒng)的正確性,這將會(huì)是一個(gè)非常困難的過程。
- 為部署而設(shè)計(jì)(design for deployment),部署耗時(shí)甚至需要停機(jī)已經(jīng)是不可接受的缺陷,部署的耗時(shí)是客戶能夠?qū)崒?shí)在在體會(huì)到的,先人一步才存在調(diào)整的可能性,落后意味著挨打已經(jīng)不是什么商業(yè)秘密,同樣客戶也不會(huì)給你停機(jī)的窗口,停機(jī)將引發(fā)海量的用戶投訴和巨大的經(jīng)濟(jì)損失。
-
為監(jiān)控而設(shè)計(jì)(design for monitor),持續(xù)交付是一個(gè)不停滾動(dòng)的過程,運(yùn)維過程無時(shí)無刻不在發(fā)生,需要摒棄過往那種當(dāng)問題發(fā)生之后才進(jìn)行處理的做法,而是采取監(jiān)控的方式提前發(fā)現(xiàn)和消滅問題。InfoQ在“Software Architecture and Design InfoQ Trends Report—April 2021”
一文中也提到了可觀測設(shè)計(jì)是一種開始得到業(yè)界認(rèn)可的設(shè)計(jì)思想。

- 為擴(kuò)展而設(shè)計(jì)(design for scale),可擴(kuò)展性不是一個(gè)新鮮的概念,這里擴(kuò)展性包含兩個(gè)方面,一是支持團(tuán)隊(duì)成員規(guī)模的擴(kuò)展;二是支持系統(tǒng)自身的擴(kuò)展。在云時(shí)代,擴(kuò)展的概念有了更多的內(nèi)涵,架構(gòu)上有了新的關(guān)注點(diǎn):彈性邊界(Elasticity Boundary)。彈性邊界是云原生(Cloud Native)架構(gòu)的核心概念,決定了我們是否能夠充分發(fā)揮云平臺(tái)的全部能力。
- 為失效而設(shè)計(jì)(design for failure),這是一種基于風(fēng)險(xiǎn)控制的架構(gòu)設(shè)計(jì),它思考的問題是“一旦部署或發(fā)布失敗,如何優(yōu)雅且快速地處理”,在很多產(chǎn)品設(shè)計(jì)中都可以看到降級(jí)處理的身影,這是一種非常典型的為失效而設(shè)計(jì)。
上述架構(gòu)要求帶來的變化使人意識(shí)到原本一個(gè)單體應(yīng)用包打天下的方法已經(jīng)有些不適用了,系統(tǒng)需要被拆分成更小的顆粒度,使其提升系統(tǒng)靈活性、可理解性并縮短開發(fā)時(shí)間,如何拆分才更合理呢?
- 組件或服務(wù)職責(zé)清晰,也是我們經(jīng)常說的SRP(單一職責(zé)原則);
- 高內(nèi)聚、低耦合,減少組件或服務(wù)獲知不相關(guān)信息的可能,其實(shí)也是我們在設(shè)計(jì)時(shí)會(huì)提到的ISP(接口隔離原則);
- 易于構(gòu)建和測試,這兩點(diǎn)是實(shí)際生產(chǎn)活動(dòng)最耗時(shí)的,不克服這兩點(diǎn)難題,無法做到“持續(xù)交付”;
- 使團(tuán)隊(duì)成員之間的溝通協(xié)作更加順暢,換句話說就是組織架構(gòu)必須適配軟件架構(gòu),這一點(diǎn)很關(guān)鍵也是常常被忽略的??低芍赋觥霸O(shè)計(jì)系統(tǒng)的組織其產(chǎn)生的設(shè)計(jì)等價(jià)于組織間的溝通結(jié)構(gòu)”,如果我們希望持續(xù)地高效、高質(zhì)量地交付有價(jià)值的軟件產(chǎn)品,那么最終還是會(huì)把視線轉(zhuǎn)向軟件生產(chǎn)者,保證他們能獲得充足的資源,信息及其流通性是其中一種重要的資源。
盡管拆分有不少好處,但也意味著構(gòu)建、測試、部署與監(jiān)測都需要隨之建立。每一個(gè)小系統(tǒng)都是一個(gè)自治系統(tǒng),都有完整的構(gòu)建、測試、部署與監(jiān)測,這樣才能在獲得系統(tǒng)拆分的好處的同時(shí)又能把控住系統(tǒng)拆分而帶來的復(fù)雜性。
常見架構(gòu)模式
基于上述的架構(gòu)要求,我們有一些可以參考使用的架構(gòu)模式。微核架構(gòu)、微服務(wù)架構(gòu)和巨石架構(gòu)是三種比較常見的架構(gòu)模式,它各有各的優(yōu)缺點(diǎn),在不同的場景下均有應(yīng)用,也都能支撐持續(xù)交付。
微核架構(gòu)又叫插件架構(gòu),顧名思義,主要業(yè)務(wù)功能和業(yè)務(wù)邏輯都通過插件實(shí)現(xiàn),核心框架部分只包含系統(tǒng)啟動(dòng)運(yùn)行的基礎(chǔ)功能,顯然這種架構(gòu)具備了:
- 功能擴(kuò)展通過插件搞定
- 獨(dú)立地發(fā)布插件,還能獨(dú)立安裝與卸載
- 功能相互隔離,易于測試
- 可定制
- 可逐步地增加功能

插件化架構(gòu)比較適合用于客戶端軟件,因此我們常用的Eclipse、IntelliJ IDEA、OSGi、Spring Plugin、SPI等都可以看作是插件化架構(gòu)產(chǎn)品。
微服務(wù)架構(gòu)是當(dāng)下非常主流的一種架構(gòu)模式,也是目前公司不少產(chǎn)品在使用的架構(gòu)模式,它的主旨就是將單一應(yīng)用劃分為一組小的服務(wù),服務(wù)之間采用輕量級(jí)的通信機(jī)制,服務(wù)圍繞業(yè)務(wù)進(jìn)行構(gòu)建可以獨(dú)立地部署,不難看出微服務(wù)具有:
- 擴(kuò)展性好,服務(wù)與服務(wù)低耦合,可以對(duì)單一服務(wù)進(jìn)行擴(kuò)容;
- 易部署
- 易開發(fā)
- 易測試
很多人把微服務(wù)架構(gòu)看成是萬靈藥,認(rèn)為所有的應(yīng)用都應(yīng)該考慮采用這種架構(gòu),這樣的想法是有問題的,微服務(wù)在提供上述的便利性的時(shí)候也會(huì)有問題,比如原子操作較困難,調(diào)用鏈長等。
Service Mesh(服務(wù)網(wǎng)格)被認(rèn)為是下一代微服務(wù)架構(gòu),Service Mesh并沒有給我們帶來新的功能,它是用于解決其他工具已經(jīng)解決過的服務(wù)網(wǎng)絡(luò)調(diào)用、限流、熔斷和監(jiān)控等問題,只不過這次是在Cloud Native 的 kubernetes 環(huán)境下的實(shí)現(xiàn)。
巨石應(yīng)用也叫單體式應(yīng)用,巨石架構(gòu),也就是由單一結(jié)構(gòu)體組成的軟件應(yīng)用,這也是我們很常見的架構(gòu)模式。當(dāng)它被良好地設(shè)計(jì)、實(shí)現(xiàn)與守護(hù)時(shí),就利于開發(fā)和調(diào)試,部署操作也很簡單,但它也通常是混亂代碼、學(xué)習(xí)困難、架構(gòu)僵硬的代表。
巨石架構(gòu)也能做到持續(xù)交付,關(guān)鍵點(diǎn)在于是否針對(duì)代碼的整個(gè)生命周期(開發(fā)、測試、部署)進(jìn)行了良好的設(shè)計(jì),否則即使是微服務(wù)這種對(duì)于持續(xù)交付天然合適的架構(gòu)也會(huì)遭遇到持續(xù)交付困難的問題。
架構(gòu)改造實(shí)施模式
當(dāng)我們遇到了不好的架構(gòu)時(shí)自然而然就需要進(jìn)行改造,改造的過程有三種很形象的比喻:
- 拆遷者,就是對(duì)軟件架構(gòu)重新設(shè)計(jì),然后一次性替換原有系統(tǒng);
- 絞殺者,保持原有遺留系統(tǒng)不變,開發(fā)新功能時(shí),逐步使遺留系統(tǒng)失效,最終實(shí)現(xiàn)替換;
- 修繕者,將遺留系統(tǒng)的部分功能與其余部分隔離,以新架構(gòu)進(jìn)行單獨(dú)改善,改善同時(shí),需保證與其他部分仍能協(xié)同工作,這種方式與絞殺者類似,但改造發(fā)生在系統(tǒng)內(nèi)部。就像我們在住的家中進(jìn)行裝修那樣會(huì)做很多隔離、鋪墊,然后再去進(jìn)行裝修。
這三種模式,最難是修繕者,需要對(duì)系統(tǒng)有很深的理解和抽象能力;最常用的是拆遷者,簡單易用,我自己就干過拆遷者的活;比較主流的是絞殺者,非常適合作為從單體系統(tǒng)向微服務(wù)遷移的策略。
絞殺者主張通過用新服務(wù)替換特定功能來將單體系統(tǒng)逐步轉(zhuǎn)換為微服務(wù),一旦新服務(wù)已經(jīng)能夠代替原有舊有功能,就將原有功能組件絞殺(即徹底停用)。基于絞殺者模式將單體系統(tǒng)拆分為微服務(wù)的時(shí)候,任何新的開發(fā)都應(yīng)該作為新服務(wù)的一部分而不是單體系統(tǒng),這保證了新的開發(fā)領(lǐng)域的代碼具有較高的質(zhì)量。
借助絞殺者模式從單體系統(tǒng)向微服務(wù)系統(tǒng)遷移可以大致簡化為三個(gè)步驟,即轉(zhuǎn)化、共存和消亡。簡而言之,就是開發(fā)一個(gè)新組件(即微服務(wù)),讓新組建和舊組件共存一段時(shí)間,最終消除舊組件。大致步驟如下:
- 最初,所有訪問應(yīng)用的流量都路由到舊版系統(tǒng);
- 構(gòu)建新組件后,可以對(duì)單體系統(tǒng)代碼與新功能一起進(jìn)行測試;
- 單體系統(tǒng)和新組件需要共同運(yùn)行一段時(shí)間,這段過渡時(shí)間可能會(huì)較?,對(duì)新組件進(jìn)行增量開發(fā)和測試后,可以完全停用舊的單體系統(tǒng)。
改造的過程還可以結(jié)合當(dāng)下主流的架構(gòu)設(shè)計(jì)方法DDD展開,我們可以先通過系統(tǒng)痛點(diǎn)分析,如代碼壞味道、系統(tǒng)或組件的耦合,發(fā)現(xiàn)我們要修訂的問題;再利用DDD提供的方法(統(tǒng)一語言、事件風(fēng)暴等)對(duì)遺留系統(tǒng)建模;當(dāng)DDD模型代碼合入的時(shí)采用絞殺者模式,最后在建立架構(gòu)度量進(jìn)行持續(xù)地守護(hù)。這樣的一套流程是許多互聯(lián)網(wǎng)企業(yè)改造他們架構(gòu)的標(biāo)準(zhǔn)做法,也是值得我們學(xué)習(xí)和借鑒的。
總結(jié)
第5章“持續(xù)交付的軟件系統(tǒng)架構(gòu)”探討了持續(xù)交付對(duì)于軟件架構(gòu)的要求,即考慮可測試、易部署、可監(jiān)測、可擴(kuò)展以及失效處理,并且把一些系統(tǒng)架構(gòu)拆分的原則“大系統(tǒng)小做”進(jìn)行了強(qiáng)調(diào),包括了職責(zé)清晰、高內(nèi)聚低耦合、易構(gòu)建與測試以及架構(gòu)相匹配的組織架構(gòu),這些都是我們在設(shè)計(jì)階段需要考慮的關(guān)鍵點(diǎn),也是影響軟件持續(xù)交付的關(guān)鍵點(diǎn)。