一文揭秘DDD到底解決了什么問(wèn)題

DDD作為架構(gòu)設(shè)計(jì)思想幫助微服務(wù)控制規(guī)模復(fù)雜度,那它是怎么做到的呢?
一、架構(gòu)設(shè)計(jì)是為了解決系統(tǒng)復(fù)雜度
談到架構(gòu),相信每個(gè)技術(shù)人員都是耳熟能詳,但如果深入探討一下,“為何要做架構(gòu)設(shè)計(jì)?”或者“架構(gòu)設(shè)計(jì)目的是什么?”類(lèi)似的問(wèn)題,大部分人可能從來(lái)沒(méi)有思考過(guò),或者即使有思考,也沒(méi)有太明確可信的答案。
1.1 架構(gòu)設(shè)計(jì)的誤區(qū)
1.1.1 每個(gè)系統(tǒng)都要做架構(gòu)設(shè)計(jì)/公司流程要求有架構(gòu)設(shè)計(jì)
知其然更要知其所以然,不能僅僅因?yàn)槠渌径荚谧黾軜?gòu)設(shè)計(jì)而盲目跟從,而應(yīng)該深入理解架構(gòu)設(shè)計(jì)的目的和必要性,根據(jù)實(shí)際需求進(jìn)行合理的設(shè)計(jì)。如果架構(gòu)師或設(shè)計(jì)師只是為了找點(diǎn)事做而進(jìn)行架構(gòu)設(shè)計(jì),不僅會(huì)浪費(fèi)時(shí)間和人力,還會(huì)拖慢整體開(kāi)發(fā)進(jìn)度。此外,其他公司的架構(gòu)設(shè)計(jì)并不一定適用于當(dāng)前項(xiàng)目,如果強(qiáng)行引入,很可能會(huì)導(dǎo)致架構(gòu)水土不服、運(yùn)行不流暢等問(wèn)題,最終需要不斷重構(gòu)或者推倒重來(lái)。因此,架構(gòu)師或設(shè)計(jì)師應(yīng)該深刻理解為何要進(jìn)行架構(gòu)設(shè)計(jì),避免生搬硬套,針對(duì)具體需求進(jìn)行合理的設(shè)計(jì),才能保證項(xiàng)目的順利進(jìn)行。
1.1.2 架構(gòu)設(shè)計(jì)是為了追求高性能、高可用、可擴(kuò)展性等單一目標(biāo)
持有這類(lèi)觀點(diǎn)的人看似具備一定架構(gòu)經(jīng)驗(yàn)或基礎(chǔ),但實(shí)際上他們無(wú)論面對(duì)什么系統(tǒng)或業(yè)務(wù),都會(huì)不顧一切地追求這些目標(biāo),導(dǎo)致架構(gòu)設(shè)計(jì)變得復(fù)雜、項(xiàng)目實(shí)施時(shí)間拖延、團(tuán)隊(duì)內(nèi)部不和等問(wèn)題的出現(xiàn)。這些問(wèn)題使得整個(gè)項(xiàng)目進(jìn)展緩慢,系統(tǒng)穩(wěn)定性差,出現(xiàn)問(wèn)題難以解決,甚至加入新功能也需要花費(fèi)大量時(shí)間。這些情況不是危言聳聽(tīng),而是廣泛出現(xiàn)的現(xiàn)象。因此,架構(gòu)師或設(shè)計(jì)師必須深入了解系統(tǒng)和業(yè)務(wù)需求,根據(jù)實(shí)際需要合理設(shè)計(jì),不可盲目追求“高XX”目標(biāo),才能確保項(xiàng)目開(kāi)發(fā)進(jìn)度和系統(tǒng)的穩(wěn)定性。?
1.2 架構(gòu)設(shè)計(jì)的真正目的
整個(gè)軟件技術(shù)發(fā)展的歷史,其實(shí)就是一部與“復(fù)雜度”斗爭(zhēng)的歷史。架構(gòu)也是為了應(yīng)對(duì)軟件系統(tǒng)復(fù)雜度而提出的一個(gè)解決方案,其主要目的是為了解決軟件系統(tǒng)復(fù)雜度帶來(lái)的問(wèn)題。
那到底什么是復(fù)雜度呢?John Ousterhout教授在 A Philosophy of Software Design 書(shū)中提到,復(fù)雜度就是任何使得軟件難于理解和修改的因素。
復(fù)雜的系統(tǒng)有一些非常明顯的特征,John教授將它抽象為變更放大(Change amplification)、認(rèn)知負(fù)荷(Cognitive load)與未知的未知(Unknown unknowns)這3類(lèi)。
變更放大(Change amplification)指得是看似簡(jiǎn)單的變更需要在許多不同地方進(jìn)行代碼修改。系統(tǒng)開(kāi)發(fā)者之前沒(méi)有及時(shí)重構(gòu)代碼,提取公共邏輯,而是省時(shí)間Ctrl-C,Ctrl-V式代碼開(kāi)發(fā)(這樣做不會(huì)影響已有的穩(wěn)定模塊,不需要做比較多的回歸測(cè)試,上線(xiàn)風(fēng)險(xiǎn)小)。當(dāng)需求變化時(shí),需要改動(dòng)多處代碼。
認(rèn)知負(fù)荷(Cognitive load)是指系統(tǒng)的學(xué)習(xí)與理解成本高,開(kāi)發(fā)人員的研發(fā)效率大大降低。
未知的未知(Unknown unknowns)是指不知道修改哪些代碼才能使系統(tǒng)功能正確的運(yùn)行,也不知道這行代碼的改動(dòng)是否會(huì)引發(fā)線(xiàn)上問(wèn)題。這一項(xiàng)是復(fù)雜性中最糟糕的一個(gè)表現(xiàn)形式。
1.3 系統(tǒng)復(fù)雜度的六個(gè)來(lái)源及通用解法
本段參考 參考5
1.高性能 2.高可用 3.可擴(kuò)展性 4.低成本 5.安全 6.規(guī)模
1.3.1 高性能
軟件系統(tǒng)中高性能帶來(lái)的復(fù)雜度主要體現(xiàn)在兩方面:
1.一方面是單臺(tái)計(jì)算機(jī)內(nèi)部為了高性能帶來(lái)的復(fù)雜度;
2.另一方面是多臺(tái)計(jì)算機(jī)集群為了高性能帶來(lái)的復(fù)雜度。
1.3.1.1 單機(jī)復(fù)雜度
計(jì)算機(jī)內(nèi)部復(fù)雜度最關(guān)鍵的地方就是操作系統(tǒng),計(jì)算機(jī)性能的發(fā)展本質(zhì)上是由硬件發(fā)展驅(qū)動(dòng)的,尤其是 CPU 的性能發(fā)展。而將硬件性能充分發(fā)揮出來(lái)的關(guān)鍵就是操作系統(tǒng),所以操作系統(tǒng)本身也是隨硬件的發(fā)展而發(fā)展的,操作系統(tǒng)是軟件系統(tǒng)的運(yùn)行環(huán)境,操作系統(tǒng)的復(fù)雜度直接決定了軟件系統(tǒng)的復(fù)雜度。
操作系統(tǒng)和性能最相關(guān)的就是進(jìn)程和線(xiàn)程。
進(jìn)程:用進(jìn)程來(lái)對(duì)應(yīng)一個(gè)操作系統(tǒng)執(zhí)行的任務(wù),每個(gè)任務(wù)都有自己獨(dú)立的內(nèi)存空間,進(jìn)程間互不相關(guān),由操作系統(tǒng)來(lái)進(jìn)行調(diào)度。
多進(jìn)程:為了達(dá)到多進(jìn)程并行運(yùn)行的目的,采取了分時(shí)的方式,即把 CPU 的時(shí)間分成很多片段,每個(gè)片段只能執(zhí)行某個(gè)進(jìn)程中的指令。
進(jìn)程間通信:為了解決進(jìn)程在運(yùn)行時(shí)相互通信的問(wèn)題,人們?cè)O(shè)計(jì)了各種進(jìn)程間通信,包括管道、消息隊(duì)列、信號(hào)量、共享存儲(chǔ)等。
多線(xiàn)程:多進(jìn)程讓多任務(wù)能夠并行處理任務(wù),但本身還有缺點(diǎn),單個(gè)進(jìn)程內(nèi)部只能串行處理,而實(shí)際上很多進(jìn)程內(nèi)部的子任務(wù)并不要求是嚴(yán)格按照時(shí)間順序來(lái)執(zhí)行的,也需要并行處理。為了解決這個(gè)問(wèn)題發(fā)明了線(xiàn)程,線(xiàn)程是進(jìn)程內(nèi)部的子任務(wù),但這些子任務(wù)都共享同一份進(jìn)程數(shù)據(jù)。為了保證數(shù)據(jù)的正確性,又發(fā)明了互斥鎖機(jī)制。有了多線(xiàn)程后,操作系統(tǒng)調(diào)度的最小單位就變成了線(xiàn)程,而進(jìn)程變成了操作系統(tǒng)分配資源的最小單位。
操作系統(tǒng)發(fā)展到現(xiàn)在,如果要完成一個(gè)高性能的軟件系統(tǒng),需要考慮如多進(jìn)程、多線(xiàn)程、進(jìn)程間通信、多線(xiàn)程并發(fā)等技術(shù)點(diǎn),而且這些技術(shù)并不是最新的就是最好的,也不是非此即彼的選擇。
在做架構(gòu)設(shè)計(jì)的時(shí)候,需要花費(fèi)很大的精力來(lái)結(jié)合業(yè)務(wù)進(jìn)行分析、判斷、選擇、組合,這個(gè)過(guò)程同樣很復(fù)雜。例如,下面的系統(tǒng)都實(shí)現(xiàn)了高性能,但是內(nèi)部實(shí)現(xiàn)差異很大:
Nginx 可以用多進(jìn)程也可以用多線(xiàn)程
JBoss 采用的是多線(xiàn)程
Redis 采用的是單進(jìn)程
Memcache 采用的是多線(xiàn)程
1.3.1.2 集群復(fù)雜度
讓多臺(tái)機(jī)器配合起來(lái)達(dá)到高性能的目的,是一個(gè)復(fù)雜的任務(wù),常見(jiàn)的方式有:任務(wù)可以指完整的業(yè)務(wù)處理,也可以指某個(gè)具體的任務(wù)。1.任務(wù)分配:每臺(tái)機(jī)器都可以處理完整的業(yè)務(wù)任務(wù),不同的任務(wù)分配到不同的機(jī)器上執(zhí)行。2.任務(wù)分解:業(yè)務(wù)越來(lái)越復(fù)雜,單臺(tái)機(jī)器處理的性能會(huì)越來(lái)越低。為了能夠繼續(xù)提升性能,采用任務(wù)分解。
1.3.1.2.1任務(wù)分配

增加一個(gè)任務(wù)分配器,可以是硬件(F5、交換機(jī))、軟件(LVS)、負(fù)載均衡軟件(Nginx、HAProxy)、自己開(kāi)發(fā)的系統(tǒng)。
任務(wù)分配器與業(yè)務(wù)服務(wù)器之間的連接和交互。
任務(wù)分配器增加分配算法(輪詢(xún)、權(quán)重、負(fù)載)。
業(yè)務(wù)量繼續(xù)提升,需要增加任務(wù)分配器的數(shù)量。

任務(wù)分配器增加為多臺(tái),這樣需要將不同的用戶(hù)請(qǐng)求分配到不同的任務(wù)分配器上(DNS輪詢(xún)、智能DNS、CDN、GSLB全局負(fù)載均衡)。
任務(wù)分配器和業(yè)務(wù)服務(wù)器之間從一對(duì)多變成多對(duì)多的網(wǎng)狀結(jié)構(gòu)。
業(yè)務(wù)服務(wù)器繼續(xù)擴(kuò)增,狀態(tài)管理和故障處理復(fù)雜度更大。
1.3.1.2.2 任務(wù)分解
微服務(wù)架構(gòu)就采用了這種思路,通過(guò)任務(wù)分配的方式,能夠突破單臺(tái)機(jī)器處理性能的瓶頸,通過(guò)增加更多的機(jī)器來(lái)滿(mǎn)足業(yè)務(wù)的性能需求,但如果業(yè)務(wù)本身也越來(lái)越復(fù)雜,單純只通過(guò)任務(wù)分配的方式來(lái)擴(kuò)展性能,收益會(huì)越來(lái)越低。?

通過(guò)這種任務(wù)分解的方式,能夠把原來(lái)大一統(tǒng)但復(fù)雜的業(yè)務(wù)系統(tǒng),拆分成小而簡(jiǎn)單但需要多個(gè)系統(tǒng)配合的業(yè)務(wù)系統(tǒng)。從業(yè)務(wù)的角度來(lái)看,任務(wù)分解既不會(huì)減少功能,也不會(huì)減少代碼量(事實(shí)上代碼量可能還會(huì)增加,因?yàn)閺拇a內(nèi)部調(diào)用改為通過(guò)服務(wù)器之間的接口調(diào)用),任務(wù)分解能夠提升性能的主要原因是:
1.簡(jiǎn)單的系統(tǒng)更容易做到高性能:系統(tǒng)的功能越簡(jiǎn)單,影響性能的點(diǎn)就越少,就更加容易進(jìn)行有針對(duì)性的優(yōu)化。
2.可以針對(duì)單個(gè)任務(wù)進(jìn)行擴(kuò)展:當(dāng)各個(gè)邏輯任務(wù)分解到獨(dú)立的子系統(tǒng)后,整個(gè)系統(tǒng)的性能瓶頸更加容易發(fā)現(xiàn),而且發(fā)現(xiàn)后只需要針對(duì)有瓶頸的子系統(tǒng)進(jìn)行性能優(yōu)化或者提升,不需要改動(dòng)整個(gè)系統(tǒng),風(fēng)險(xiǎn)會(huì)小很多。
最終決定業(yè)務(wù)處理性能的還是業(yè)務(wù)邏輯本身,業(yè)務(wù)邏輯本身沒(méi)有發(fā)生大的變化下,理論上的性能是有一個(gè)上限的,系統(tǒng)拆分能夠讓性能逼近這個(gè)極限,但無(wú)法突破這個(gè)極限。
1.3.2 高可用
系統(tǒng)無(wú)中斷地執(zhí)行其功能的能力,代表系統(tǒng)的可用性程度,是進(jìn)行系統(tǒng)設(shè)計(jì)時(shí)的準(zhǔn)則之一。
本質(zhì)上都是通過(guò)“冗余”來(lái)實(shí)現(xiàn)高可用。高可用的“冗余”解決方案,單純從形式上來(lái)看,和高性能是一樣的,都是通過(guò)增加更多機(jī)器來(lái)達(dá)到目的,但其實(shí)本質(zhì)上是有根本區(qū)別的:高性能增加機(jī)器目的在于“擴(kuò)展”處理性能;高可用增加機(jī)器目的在于“冗余”處理單元。
通過(guò)冗余增強(qiáng)了可用性,但同時(shí)也帶來(lái)了復(fù)雜性。
1.3.2.1 計(jì)算高可用
計(jì)算的特點(diǎn)是無(wú)論從哪臺(tái)機(jī)器上進(jìn)行計(jì)算,同樣的算法和輸入數(shù)據(jù),產(chǎn)出的結(jié)果都是一樣的,所以將計(jì)算從一臺(tái)機(jī)器遷移到另一臺(tái)對(duì)業(yè)務(wù)沒(méi)有影響。

需要增加一個(gè)任務(wù)分配器
任務(wù)分配器和真正的業(yè)務(wù)服務(wù)器之間有連接和交互
任務(wù)分配器需要增加分配算法(主備【冷備、溫備、熱備】、主主、多主多倍【2主2備、4主0備】)
1.3.2.2 存儲(chǔ)高可用
對(duì)于需要存儲(chǔ)數(shù)據(jù)的系統(tǒng)而言,整個(gè)系統(tǒng)的高可用設(shè)計(jì)的難點(diǎn)和關(guān)鍵點(diǎn)在于“存儲(chǔ)高可用”。存儲(chǔ)和計(jì)算的本質(zhì)區(qū)別在于將數(shù)據(jù)從一臺(tái)機(jī)器搬移到另一臺(tái)機(jī)器時(shí)需要通過(guò)線(xiàn)路進(jìn)行傳輸,而線(xiàn)路傳輸是存在延遲的,速度在毫秒級(jí)別,距離越遠(yuǎn),延遲越高。加之各種異常情況(如傳輸中斷、丟包、擁塞),會(huì)導(dǎo)致延遲更高。對(duì)于高可用系統(tǒng)來(lái)說(shuō),在某個(gè)時(shí)間點(diǎn)通信中斷就意味著整個(gè)系統(tǒng)的數(shù)據(jù)不一致。按照“數(shù)據(jù) + 邏輯 = 業(yè)務(wù)”的公式,數(shù)據(jù)不一致將導(dǎo)致最終業(yè)務(wù)表現(xiàn)不同。如果不做冗余備份,系統(tǒng)的整體高可用性無(wú)法保證。因此,存儲(chǔ)高可用的難點(diǎn)不在于如何備份數(shù)據(jù),而在于如何減少或規(guī)避數(shù)據(jù)不一致對(duì)業(yè)務(wù)造成的影響。
分布式領(lǐng)域內(nèi)著名的 CAP 定理從理論上證實(shí)了存儲(chǔ)高可用的復(fù)雜度。存儲(chǔ)高可用不可能同時(shí)滿(mǎn)足“一致性、可用性、分區(qū)容忍性”,最多只能滿(mǎn)足其中兩個(gè)。因此,在進(jìn)行架構(gòu)設(shè)計(jì)時(shí)需要結(jié)合業(yè)務(wù)進(jìn)行取舍。
1.3.3 可擴(kuò)展性
可擴(kuò)展性指系統(tǒng)為了應(yīng)對(duì)將來(lái)需求變化而提供的一種擴(kuò)展能力,當(dāng)有新的需求出現(xiàn)時(shí),系統(tǒng)不需要或者僅需要少量修改就可以支持,無(wú)須整個(gè)系統(tǒng)重構(gòu)或者重建。
在軟件開(kāi)發(fā)領(lǐng)域,面向?qū)ο笏枷氲奶岢?,就是為了解決可擴(kuò)展性帶來(lái)的問(wèn)題;設(shè)計(jì)模式更是將可擴(kuò)展性做到了極致。
設(shè)計(jì)具備良好可擴(kuò)展性的系統(tǒng),有兩個(gè)基本條件:
正確預(yù)測(cè)變化
完美封裝變化
1.3.3.1 預(yù)測(cè)變化
“唯一不變的是變化”,按照這個(gè)標(biāo)準(zhǔn)衡量,架構(gòu)師每個(gè)設(shè)計(jì)方案都要考慮可擴(kuò)展性。預(yù)測(cè)變化的復(fù)雜性在于:
不能每個(gè)設(shè)計(jì)點(diǎn)都考慮可擴(kuò)展性
不能完全不考慮擴(kuò)展性
所有的預(yù)測(cè)都存在出錯(cuò)的可能性
如何把握預(yù)測(cè)的程度和提升預(yù)測(cè)結(jié)果的準(zhǔn)確性,是一件很復(fù)雜的事情,而且沒(méi)有通用的標(biāo)準(zhǔn),更多是靠經(jīng)驗(yàn)、直覺(jué)。
1.3.3.2 應(yīng)對(duì)變化
預(yù)測(cè)變化是一回事,采取什么方案來(lái)應(yīng)對(duì)變化,又是另外一個(gè)復(fù)雜的事情。即使預(yù)測(cè)很準(zhǔn)確,如果方案不合適,則系統(tǒng)擴(kuò)展一樣很麻煩。
微服務(wù)架構(gòu)中的各層進(jìn)行封裝和隔離也是一種應(yīng)對(duì)變化的解決方式。
1.3.3.2.1 變化層VS穩(wěn)定層
第一種應(yīng)對(duì)變化的常見(jiàn)方案是將“變化”封裝在一個(gè)“變化層”,將不變的部分封裝在一個(gè)獨(dú)立的“穩(wěn)定層”。
無(wú)論是變化層依賴(lài)穩(wěn)定層,還是穩(wěn)定層依賴(lài)變化層都是可以的,需要根據(jù)具體業(yè)務(wù)情況來(lái)設(shè)計(jì)。
無(wú)論采取哪種形式,通過(guò)剝離變化層和穩(wěn)定層的方式應(yīng)對(duì)變化,都會(huì)帶來(lái)兩個(gè)主要的復(fù)雜性相關(guān)的問(wèn)題。
1.系統(tǒng)需要拆分出變化層和穩(wěn)定層(如何拆分)
2.需要設(shè)計(jì)變化層和穩(wěn)定層之間的接口(穩(wěn)定層接口越穩(wěn)定越好,變化層接口從差異中找到共同點(diǎn))
1.3.3.2.2 抽象層VS實(shí)現(xiàn)層
第二種常見(jiàn)的應(yīng)對(duì)變化的方案是提煉出一個(gè)“抽象層”和一個(gè)“實(shí)現(xiàn)層”。
抽象層是穩(wěn)定的,實(shí)現(xiàn)層可以根據(jù)具體業(yè)務(wù)需要定制開(kāi)發(fā),當(dāng)加入新的功能時(shí),只需要增加新的實(shí)現(xiàn),無(wú)須修改抽象層。這種方案典型的實(shí)踐就是策略模式。
1.3.4 低成本
在設(shè)計(jì)高性能、高可用的架構(gòu)方案時(shí),如果涉及到數(shù)百、數(shù)千甚至數(shù)萬(wàn)臺(tái)服務(wù)器,成本就會(huì)成為一個(gè)非常重要的考慮點(diǎn)。為了控制成本,需要減少服務(wù)器的數(shù)量,這與增加更多服務(wù)器來(lái)提升性能和可用性的通用做法相沖突。因此,低成本往往不是架構(gòu)設(shè)計(jì)的首要目標(biāo),而是一個(gè)附加約束。為了解決這個(gè)問(wèn)題,需要先設(shè)定一個(gè)成本目標(biāo),然后根據(jù)高性能和高可用的要求設(shè)計(jì)方案,并評(píng)估是否能夠滿(mǎn)足成本目標(biāo)。如果不能,就需要重新設(shè)計(jì)架構(gòu);如果無(wú)論如何都無(wú)法設(shè)計(jì)出滿(mǎn)足成本要求的方案,那只能找老板調(diào)整成本目標(biāo)了。低成本給架構(gòu)設(shè)計(jì)帶來(lái)的主要復(fù)雜度體現(xiàn)在,往往只有"創(chuàng)新"才能達(dá)到低成本目標(biāo)。"創(chuàng)新"的含義是開(kāi)創(chuàng)一個(gè)全新的技術(shù)領(lǐng)域,或者引入新技術(shù)來(lái)解決問(wèn)題。如果沒(méi)有找到能夠解決自己?jiǎn)栴}的新技術(shù),那么就需要自己創(chuàng)造新技術(shù)了。例如,NoSQL(如Memcache、Redis等)是為了解決關(guān)系型數(shù)據(jù)庫(kù)無(wú)法應(yīng)對(duì)高并發(fā)訪(fǎng)問(wèn)帶來(lái)的訪(fǎng)問(wèn)壓力;全文搜索引擎(如Sphinx、Elasticsearch、Solr)是為了解決關(guān)系型數(shù)據(jù)庫(kù)like搜索的低效問(wèn)題;Hadoop則是為了解決傳統(tǒng)文件系統(tǒng)無(wú)法應(yīng)對(duì)海量數(shù)據(jù)存儲(chǔ)和計(jì)算的問(wèn)題。創(chuàng)造新技術(shù)的主要復(fù)雜度在于需要?jiǎng)?chuàng)造全新的理念和技術(shù),并且新技術(shù)需要與舊技術(shù)相比有質(zhì)的飛躍。
1.3.5 安全
從技術(shù)的角度來(lái)講,安全可以分為兩類(lèi):
一類(lèi)是功能上的安全,
一類(lèi)是架構(gòu)上的安全。
1.3.5.1 功能安全
常見(jiàn)的 XSS 攻擊、CSRF 攻擊、SQL 注入、Windows 漏洞、密碼破解等,本質(zhì)上是因?yàn)橄到y(tǒng)實(shí)現(xiàn)有漏洞,黑客有了可乘之機(jī),功能安全其實(shí)就是“防小偷”。
從實(shí)現(xiàn)的角度來(lái)看,功能安全更多地是和具體的編碼相關(guān),與架構(gòu)關(guān)系不大。開(kāi)發(fā)框架會(huì)內(nèi)嵌常見(jiàn)的安全功能,但是開(kāi)發(fā)框架本身也可能存在安全漏洞和風(fēng)險(xiǎn)。
所以功能安全是一個(gè)逐步完善的過(guò)程,而且往往都是在問(wèn)題出現(xiàn)后才能有針對(duì)性的提出解決方案,我們永遠(yuǎn)無(wú)法預(yù)測(cè)系統(tǒng)下一個(gè)漏洞在哪里,也不敢說(shuō)自己的系統(tǒng)肯定沒(méi)有任何問(wèn)題。
換句話(huà)講,功能安全其實(shí)也是一個(gè)“攻”與“防”的矛盾,只能在這種攻防大戰(zhàn)中逐步完善,不可能在系統(tǒng)架構(gòu)設(shè)計(jì)的時(shí)候一勞永逸地解決。
1.3.5.2 架構(gòu)安全
如果說(shuō)功能安全是“防小偷”,那么架構(gòu)安全就是“防強(qiáng)盜”。
架構(gòu)設(shè)計(jì)時(shí)需要特別關(guān)注架構(gòu)安全,尤其是互聯(lián)網(wǎng)時(shí)代,理論上來(lái)說(shuō)系統(tǒng)部署在互聯(lián)網(wǎng)上時(shí),全球任何地方都可以發(fā)起攻擊。
傳統(tǒng)的架構(gòu)安全主要依靠防火墻,防火墻最基本的功能就是隔離網(wǎng)絡(luò),通過(guò)將網(wǎng)絡(luò)劃分成不同的區(qū)域,制定出不同區(qū)域之間的訪(fǎng)問(wèn)控制策略來(lái)控制不同信任程度區(qū)域間傳送的數(shù)據(jù)流。
防火墻的功能雖然強(qiáng)大,但性能一般,所以在傳統(tǒng)的銀行和企業(yè)應(yīng)用領(lǐng)域應(yīng)用較多。但在互聯(lián)網(wǎng)領(lǐng)域,防火墻的應(yīng)用場(chǎng)景并不多。
互聯(lián)網(wǎng)系統(tǒng)的架構(gòu)安全目前并沒(méi)有太好的設(shè)計(jì)手段來(lái)實(shí)現(xiàn),更多地是依靠運(yùn)營(yíng)商或者云服務(wù)商強(qiáng)大的帶寬和流量清洗的能力,較少自己來(lái)設(shè)計(jì)和實(shí)現(xiàn)。
1.3.6 規(guī)模
規(guī)模帶來(lái)復(fù)雜度的主要原因就是“量變引起質(zhì)變”,當(dāng)數(shù)量超過(guò)一定的閾值后,復(fù)雜度會(huì)發(fā)生質(zhì)的變化。常見(jiàn)的規(guī)模帶來(lái)的復(fù)雜度有:
1.功能越來(lái)越多,系統(tǒng)復(fù)雜度指數(shù)級(jí)上升
2.數(shù)據(jù)越來(lái)越多,系統(tǒng)復(fù)雜度發(fā)生質(zhì)變
1.3.6.1 功能越來(lái)越多,系統(tǒng)復(fù)雜度指數(shù)級(jí)上升
例如,某個(gè)系統(tǒng)開(kāi)始只有 3 大功能,后來(lái)不斷增加到 8 大功能,雖然還是同一個(gè)系統(tǒng),但復(fù)雜度已經(jīng)相差很大了,具體相差多大呢?我以一個(gè)簡(jiǎn)單的抽象模型來(lái)計(jì)算一下,假設(shè)系統(tǒng)間的功能都是兩兩相關(guān)的,系統(tǒng)的復(fù)雜度 = 功能數(shù)量 + 功能之間的連接數(shù)量,通過(guò)計(jì)算我們可以看出:3個(gè)功能的系統(tǒng)復(fù)雜度為3+3=6
8個(gè)功能的系統(tǒng)復(fù)雜度為8+(7+0)*8/2=36 可以看出,具備8個(gè)功能的系統(tǒng)的復(fù)雜度不是比具備 3 個(gè)功能的系統(tǒng)的復(fù)雜度多5,而是多了30,基本是指數(shù)級(jí)增長(zhǎng)的,主要原因在于隨著系統(tǒng)功能數(shù)量增多,功能之間的連接呈指數(shù)級(jí)增長(zhǎng)。下圖形象地展示了功能數(shù)量的增多帶來(lái)了復(fù)雜度。

1.3.6.2 數(shù)據(jù)越來(lái)越多,系統(tǒng)復(fù)雜度發(fā)生質(zhì)變
隨著數(shù)據(jù)量的不斷增長(zhǎng),傳統(tǒng)的數(shù)據(jù)處理和管理方式已經(jīng)無(wú)法適應(yīng),因此“大數(shù)據(jù)”這一概念應(yīng)運(yùn)而生。大數(shù)據(jù)的誕生主要是為了解決數(shù)據(jù)規(guī)模變得越來(lái)越大時(shí),傳統(tǒng)的數(shù)據(jù)收集、存儲(chǔ)、分析等方式無(wú)法勝任的問(wèn)題。Google的三篇技術(shù)論文,即Google File System、Google Bigtable和Google MapReduce則分別開(kāi)創(chuàng)了大數(shù)據(jù)文件存儲(chǔ)、列式數(shù)據(jù)存儲(chǔ)和大數(shù)據(jù)運(yùn)算的技術(shù)領(lǐng)域。即便數(shù)據(jù)規(guī)模沒(méi)有達(dá)到大數(shù)據(jù)的水平,數(shù)據(jù)增長(zhǎng)仍然可能會(huì)給系統(tǒng)帶來(lái)復(fù)雜性。例如,在使用關(guān)系數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù)時(shí),當(dāng)單表數(shù)據(jù)達(dá)到一定規(guī)模時(shí),就會(huì)導(dǎo)致添加索引、修改表結(jié)構(gòu)等操作變得很慢,可能需要幾個(gè)小時(shí),這就會(huì)對(duì)業(yè)務(wù)造成不良影響。因此,必須考慮將單表拆分為多表來(lái)解決這個(gè)問(wèn)題,但這個(gè)過(guò)程也會(huì)引入更多的復(fù)雜性。
1.4 簡(jiǎn)單的復(fù)雜度分析案例
我們來(lái)分析一個(gè)簡(jiǎn)單的案例,一起來(lái)看看如何將“架構(gòu)設(shè)計(jì)的真正目的是為了解決軟件系統(tǒng)復(fù)雜度帶來(lái)的問(wèn)題”這個(gè)指導(dǎo)思想應(yīng)用到實(shí)踐中。
當(dāng)我們?cè)O(shè)計(jì)一個(gè)大學(xué)的學(xué)生管理系統(tǒng)時(shí),我們需要考慮該系統(tǒng)的復(fù)雜度以及如何解決這些復(fù)雜度帶來(lái)的問(wèn)題。首先,我們可以將該系統(tǒng)的復(fù)雜度分為以下幾個(gè)方面:
性能 該系統(tǒng)的訪(fǎng)問(wèn)頻率并不高,因此性能并不是一個(gè)很大的問(wèn)題。我們可以使用MySQL作為存儲(chǔ),Nginx作為Web服務(wù)器,無(wú)需考慮緩存。
可擴(kuò)展性 該系統(tǒng)的功能比較穩(wěn)定,可擴(kuò)展的空間并不大,因此可擴(kuò)展性方面也不是一個(gè)很大的問(wèn)題。
高可用 數(shù)據(jù)丟失是不可接受的,故該系統(tǒng)的高可用性方面需要考慮多種異常情況,如機(jī)器故障、機(jī)房故障等。為此,我們需要設(shè)計(jì)MySQL同機(jī)房主備方案和MySQL跨機(jī)房同步方案。
安全性 該系統(tǒng)存儲(chǔ)的信息涉及到學(xué)生的隱私,因此需要考慮安全性。我們可以使用Nginx提供的ACL控制、用戶(hù)賬號(hào)密碼管理和數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)權(quán)限控制來(lái)保證系統(tǒng)的安全性。
成本 由于該系統(tǒng)比較簡(jiǎn)單,基本上幾臺(tái)服務(wù)器就可以搞定,因此成本方面并不需要太多關(guān)注。
規(guī)模 同上,規(guī)模復(fù)雜度無(wú)需過(guò)度關(guān)注。
總體來(lái)說(shuō),我們需要在架構(gòu)設(shè)計(jì)中充分考慮系統(tǒng)的復(fù)雜度,同時(shí)根據(jù)不同問(wèn)題選擇合適的解決方案,以提高系統(tǒng)的可靠性和安全性。
1.5 總結(jié)
第一章提出了架構(gòu)的根本目是解決系統(tǒng)復(fù)雜度,并簡(jiǎn)要說(shuō)明系統(tǒng)復(fù)雜度的六個(gè)來(lái)源及通用解法,為我們?cè)O(shè)計(jì)架構(gòu)提供了清晰可執(zhí)行的操作思路。
二、微服務(wù)架構(gòu)解決了高可用、可擴(kuò)展問(wèn)題,但性能下降、
成本&規(guī)模復(fù)雜度暴增
我們知道,這些年來(lái)隨著設(shè)備和新技術(shù)的發(fā)展,軟件的架構(gòu)模式發(fā)生了很大的變化。軟件架構(gòu)模式大體來(lái)說(shuō)經(jīng)歷了從單機(jī)、集中式到分布式微服務(wù)架構(gòu)三個(gè)階段的演進(jìn) 。隨著分布式技術(shù)的快速興起,我們已經(jīng)進(jìn)入到了微服務(wù)架構(gòu)時(shí)代。

2.1 微服務(wù)架構(gòu)的優(yōu)點(diǎn)
與傳統(tǒng)單體應(yīng)用架構(gòu)相比,微服務(wù)架構(gòu)有很多優(yōu)點(diǎn),具體表現(xiàn)如下:
2.1.1 高可用
當(dāng)架構(gòu)中的某一組件發(fā)生故障時(shí),在單一進(jìn)程的傳統(tǒng)架構(gòu)下,故障很有可能在進(jìn)程內(nèi)擴(kuò)散,導(dǎo)致整個(gè)應(yīng)用不可用。在微服務(wù)架構(gòu)下,故障會(huì)被隔離在單個(gè)服務(wù)中。若設(shè)計(jì)良好,其他服務(wù)可通過(guò)重試、平穩(wěn)退化等機(jī)制實(shí)現(xiàn)應(yīng)用層面的容錯(cuò)。
2.1.4 可擴(kuò)展
單個(gè)服務(wù)應(yīng)用也可以實(shí)現(xiàn)橫向擴(kuò)展,這種擴(kuò)展可以通過(guò)將整個(gè)應(yīng)用完整的復(fù)制到不同的節(jié)點(diǎn)中實(shí)現(xiàn)。當(dāng)應(yīng)用的不同組件在擴(kuò)展需求上存在差異時(shí),微服務(wù)架構(gòu)便體現(xiàn)出其靈活性,因?yàn)槊總€(gè)服務(wù)可以根據(jù)實(shí)際需求獨(dú)立進(jìn)行擴(kuò)展。
2.2 微服務(wù)的缺點(diǎn)
2.2.1 復(fù)雜度高
與單體式架構(gòu)相比,微服務(wù)會(huì)導(dǎo)致復(fù)雜性上升,因?yàn)槎鄠€(gè)團(tuán)隊(duì)會(huì)在更多地方創(chuàng)建更多服務(wù)。若管理不當(dāng),則會(huì)導(dǎo)致開(kāi)發(fā)速度和效率降低。
2.2.2 基礎(chǔ)設(shè)施成本呈指數(shù)級(jí)增長(zhǎng)
每個(gè)新的微服務(wù)都有自己的成本,例如測(cè)試工具、托管基礎(chǔ)架構(gòu)和監(jiān)控工具等方面。
2.2.3 性能下降
微服務(wù)之間通過(guò)REST、RPC等形式進(jìn)行交互,通信的時(shí)延會(huì)受到較大的影響。
三、DDD幫助微服務(wù)控制規(guī)模復(fù)雜度
3.1 控制成本&規(guī)模復(fù)雜度需要明晰微服務(wù)的邊界
由1.3.6.1 所述,隨著微服務(wù)數(shù)量上升,規(guī)模復(fù)雜度呈指數(shù)級(jí)上升,那么我們理應(yīng)控制微服務(wù)的數(shù)量,這就帶來(lái)了爭(zhēng)論和疑惑:微服務(wù)的粒度應(yīng)該多大呀?微服務(wù)到底應(yīng)該如何拆分和設(shè)計(jì)呢?
長(zhǎng)期以來(lái),微服務(wù)架構(gòu)缺乏一套系統(tǒng)的理論和方法來(lái)指導(dǎo)其拆分,這導(dǎo)致了一些人對(duì)微服務(wù)架構(gòu)的理解出現(xiàn)了一些曲解。有人簡(jiǎn)單地認(rèn)為微服務(wù)只是將原來(lái)的單體應(yīng)用程序拆分為多個(gè)部署包或更換為支持微服務(wù)架構(gòu)的技術(shù)框架,便可成為微服務(wù)。還有人認(rèn)為,微服務(wù)越小越好。
然而,在過(guò)去的幾年中,一些項(xiàng)目由于在前期過(guò)度拆分微服務(wù),導(dǎo)致項(xiàng)目復(fù)雜度過(guò)高,無(wú)法進(jìn)行上線(xiàn)和運(yùn)維的情況已經(jīng)出現(xiàn)。綜合來(lái)看,我認(rèn)為微服務(wù)拆分困境的根本原因在于不清楚業(yè)務(wù)或微服務(wù)的邊界在哪里。換句話(huà)說(shuō),只有確定了業(yè)務(wù)邊界和應(yīng)用邊界,這個(gè)困境才能迎刃而解。
3.2 DDD能夠幫我們?cè)O(shè)計(jì)出清晰的領(lǐng)域和應(yīng)用邊界
DDD 包括戰(zhàn)略設(shè)計(jì)和戰(zhàn)術(shù)設(shè)計(jì)兩部分。戰(zhàn)略設(shè)計(jì)主要從業(yè)務(wù)視角出發(fā),建立業(yè)務(wù)領(lǐng)域模型,劃分領(lǐng)域邊界,建立通用語(yǔ)言的限界上下文,這些限界上下文可以作為微服務(wù)設(shè)計(jì)的參考邊界。而戰(zhàn)術(shù)設(shè)計(jì)則從技術(shù)視角出發(fā),著重于領(lǐng)域模型的技術(shù)實(shí)現(xiàn),包括聚合根、實(shí)體、值對(duì)象、領(lǐng)域服務(wù)、應(yīng)用服務(wù)和資源庫(kù)等代碼邏輯的設(shè)計(jì)和實(shí)現(xiàn)。
在戰(zhàn)略設(shè)計(jì)過(guò)程中,領(lǐng)域模型的建立是重中之重。為此,DDD 提出了事件風(fēng)暴這一建立領(lǐng)域模型的方法。事件風(fēng)暴是一個(gè)從發(fā)散到收斂的過(guò)程,通常采用用例分析、場(chǎng)景分析和用戶(hù)旅程分析,全面分解業(yè)務(wù)領(lǐng)域,梳理領(lǐng)域?qū)ο笾g的關(guān)系,這是一個(gè)發(fā)散的過(guò)程。在事件風(fēng)暴過(guò)程中,會(huì)產(chǎn)生很多實(shí)體、命令、事件等領(lǐng)域?qū)ο?,我們將這些領(lǐng)域?qū)ο髲牟煌木S度進(jìn)行聚類(lèi),形成如聚合、限界上下文等邊界,建立領(lǐng)域模型,這就是一個(gè)收斂的過(guò)程。
因此,DDD 可以幫助軟件工程師建立清晰的領(lǐng)域模型,劃分業(yè)務(wù)和應(yīng)用邊界,以指導(dǎo)微服務(wù)的設(shè)計(jì)和拆分。事件風(fēng)暴是建立領(lǐng)域模型的主要方法,通過(guò)其發(fā)散的過(guò)程和聚合的過(guò)程,建立起合理的領(lǐng)域模型,從而實(shí)現(xiàn)高效的軟件開(kāi)發(fā)和落地。

我們可以用三步來(lái)劃定領(lǐng)域模型和微服務(wù)的邊界。
在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的戰(zhàn)略設(shè)計(jì)中,我們采用事件風(fēng)暴方法梳理業(yè)務(wù)過(guò)程中的用戶(hù)操作、事件以及外部依賴(lài)關(guān)系等,從而梳理出領(lǐng)域?qū)嶓w等領(lǐng)域?qū)ο蟆?/p>
然后,我們根據(jù)領(lǐng)域?qū)嶓w之間的業(yè)務(wù)關(guān)聯(lián)性,形成聚合,并確定聚合中的聚合根、值對(duì)象和實(shí)體。
接著,根據(jù)業(yè)務(wù)及語(yǔ)義邊界等因素,將一個(gè)或多個(gè)聚合劃定在一個(gè)限界上下文內(nèi),形成領(lǐng)域模型。這個(gè)過(guò)程中,我們建立了領(lǐng)域模型,劃定了業(yè)務(wù)領(lǐng)域的邊界,建立了通用語(yǔ)言和限界上下文,確定了領(lǐng)域模型中各個(gè)領(lǐng)域?qū)ο蟮年P(guān)系。這些限界上下文可以作為微服務(wù)設(shè)計(jì)的參考邊界,從而確定了應(yīng)用端的微服務(wù)邊界。
在從業(yè)務(wù)模型向微服務(wù)落地的過(guò)程中,即從戰(zhàn)略設(shè)計(jì)向戰(zhàn)術(shù)設(shè)計(jì)的實(shí)施過(guò)程中,我們會(huì)將領(lǐng)域模型中的領(lǐng)域?qū)ο笈c代碼模型中的代碼對(duì)象建立映射關(guān)系,將業(yè)務(wù)架構(gòu)和系統(tǒng)架構(gòu)進(jìn)行綁定。當(dāng)調(diào)整業(yè)務(wù)架構(gòu)和領(lǐng)域模型以響應(yīng)業(yè)務(wù)變化時(shí),系統(tǒng)架構(gòu)也會(huì)同時(shí)發(fā)生調(diào)整,并同步建立新的映射關(guān)系。這種方式可以幫助我們實(shí)現(xiàn)高效的軟件開(kāi)發(fā)和落地,從而更好地應(yīng)對(duì)業(yè)務(wù)需求變化。
因此,通過(guò)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的戰(zhàn)略設(shè)計(jì)和戰(zhàn)術(shù)設(shè)計(jì),我們可以清晰地劃定領(lǐng)域邊界,建立領(lǐng)域模型,幫助我們實(shí)現(xiàn)微服務(wù)的設(shè)計(jì)和拆分,同時(shí)也能夠有效地響應(yīng)業(yè)務(wù)變化,提高軟件開(kāi)發(fā)和落地的效率。
3.3 微服務(wù)與DDD的異同
DDD 是一種面向復(fù)雜業(yè)務(wù)領(lǐng)域的設(shè)計(jì)方法論,而微服務(wù)是一種面向分布式系統(tǒng)的架構(gòu)風(fēng)格。它們的共同目標(biāo)都是通過(guò)將系統(tǒng)分解為更小,更易于管理的組件來(lái)提高系統(tǒng)的可維護(hù)性和可擴(kuò)展性。
在 DDD 中,我們關(guān)注的是業(yè)務(wù)領(lǐng)域的劃分和領(lǐng)域模型的設(shè)計(jì),以便更好地理解業(yè)務(wù)需求,并將其轉(zhuǎn)化為可執(zhí)行的代碼。
在微服務(wù)中,我們主要關(guān)注的是運(yùn)行時(shí)的通信,容錯(cuò)和故障隔離,以及服務(wù)治理等問(wèn)題,以確保各個(gè)微服務(wù)可以獨(dú)立開(kāi)發(fā)、測(cè)試、構(gòu)建和部署。
通過(guò)結(jié)合 DDD 和微服務(wù),我們可以更好地實(shí)現(xiàn)高效的業(yè)務(wù)邏輯,同時(shí)也可以充分利用微服務(wù)的優(yōu)點(diǎn),提高應(yīng)用程序的可擴(kuò)展性和可維護(hù)性。因此,DDD 和微服務(wù)是互補(bǔ)的,可以共同用于構(gòu)建可靠且高效的應(yīng)用程序。
