基于微服務(wù)的企業(yè)應(yīng)用架構(gòu)設(shè)計(jì)范式
這個(gè)話題曾經(jīng)分別在PWorld大會(huì)和QCon2016大會(huì)上做過分享,得到不錯(cuò)的反響,今天終于有時(shí)間整理到博客上了。
微服務(wù)好像是這兩年突然火起來的,其實(shí)和很多其他架構(gòu)風(fēng)格一樣,微服務(wù)架構(gòu)也是我們?cè)谟密浖淖兪澜绲倪^程中,為了適應(yīng)內(nèi)外部環(huán)境的變化,而逐漸演化出的一種當(dāng)前的最佳實(shí)踐。
比如SOA,比如J2EE,比如傳統(tǒng)分布式;微服務(wù)架構(gòu)和它們都有千絲萬縷的聯(lián)系。
流水記錄了業(yè)務(wù)狀態(tài)最終確定前的整個(gè)過程,是給業(yè)務(wù)參與各方看的,這個(gè)參與各方包括了客戶(比如大家拿到的信用卡刷卡記錄)、第三方系統(tǒng)(比如對(duì)賬文件)、內(nèi)部用戶(比如我們給客服打電話,客服可以知道你的交易歷史)等等。
由于流水的作用和系統(tǒng)日志非常像,因此有些系統(tǒng)在設(shè)計(jì)時(shí)會(huì)把這兩者混淆起來,基于性能的考慮,會(huì)像記錄日志那樣,用異步方式來記錄流水。其實(shí)這是非常大的誤區(qū)。就像上面所說,流水有業(yè)務(wù)含義,是給業(yè)務(wù)參與各方看的,它所記錄的數(shù)據(jù)應(yīng)該是和業(yè)務(wù)數(shù)據(jù)有一致性要求,而日志是給我們程序員看的,偶爾丟個(gè)一兩條是可接受的。
因此我們需要用同步的方式來記錄業(yè)務(wù)流水,也就是說記錄流水應(yīng)該和正常的業(yè)務(wù)操作在同一個(gè)事務(wù)中。
上圖中,第一個(gè)“內(nèi)部操作”更改了業(yè)務(wù)數(shù)據(jù)狀態(tài),因此需要同步記錄業(yè)務(wù)流水,其他幾個(gè)環(huán)節(jié)只記錄日志即可。
范式二、流水號(hào)設(shè)計(jì)的GAIR模式
在記錄流水和日志的過程中,我們需要唯一標(biāo)識(shí)一筆服務(wù)調(diào)用。站在IT全局的視角來看,我們需要記錄并能在適當(dāng)?shù)臅r(shí)候還原整個(gè)調(diào)用鏈。出現(xiàn)數(shù)據(jù)不一致時(shí),我們要進(jìn)行補(bǔ)償操作,又需要能夠定位錯(cuò)誤發(fā)生時(shí)的數(shù)據(jù)狀態(tài)……
這些都需要以“流水號(hào)”為基礎(chǔ),這就帶來了微服務(wù)架構(gòu)的第二個(gè)范式——流水號(hào)的GAIR(蓋爾)模式
G、A、I、R分別代表:Global_ID(全局流水號(hào))、Answer_ID(響應(yīng)流水號(hào))、InRequest_ID(外部請(qǐng)求流水號(hào))、Request_ID(內(nèi)部請(qǐng)求流水號(hào))
這四個(gè)流水號(hào)的產(chǎn)生方、產(chǎn)生時(shí)機(jī)各不相同,下圖展示了它們之間的區(qū)別和聯(lián)系。
范式三、元數(shù)據(jù)驅(qū)動(dòng)的微服務(wù)定義
以元數(shù)據(jù)的方式定義微服務(wù)帶來兩個(gè)好處。
一、機(jī)器可讀,這給未來全面自動(dòng)化提供了前提條件。
二、標(biāo)準(zhǔn)統(tǒng)一,這給服務(wù)在整個(gè)交付環(huán)節(jié)中的橫向打通提供了支撐。
我們?cè)趯?shí)踐中,以元數(shù)據(jù)方式,定義了服務(wù)接口數(shù)據(jù)的結(jié)構(gòu)、每個(gè)數(shù)據(jù)項(xiàng)所遵循的數(shù)據(jù)標(biāo)準(zhǔn)、每個(gè)服務(wù)接收請(qǐng)求數(shù)據(jù)時(shí)的校驗(yàn)規(guī)則和值轉(zhuǎn)換規(guī)則等等。
這些元數(shù)據(jù)提供給專門的服務(wù)治理系統(tǒng)、數(shù)據(jù)治理系統(tǒng)、DevOps平臺(tái),從而構(gòu)建出數(shù)字化IT。
在移動(dòng)互聯(lián)的外部環(huán)境中,微服務(wù)化的IT系統(tǒng)如何應(yīng)對(duì)不確定的并發(fā)請(qǐng)求、超量請(qǐng)求?同時(shí)還要兼顧我們所連接的外部系統(tǒng)的網(wǎng)絡(luò)中斷、宕機(jī)等服務(wù)不可用、超時(shí)等一系列問題。
要解決這些問題,需要運(yùn)用我們的第四個(gè)范式:以異步的方式處理同步調(diào)用。
在實(shí)踐中,我們所使用的異步方式和傳統(tǒng)異步不太一樣。
傳統(tǒng)基于事件的異步,每個(gè)并發(fā)流作為一個(gè)有限狀態(tài)機(jī),應(yīng)用直接控制并發(fā),隨著負(fù)載的增加,吞吐量會(huì)飽和,響應(yīng)時(shí)間也會(huì)線性增長。
我們使用SEDA(Staged Event Driven Architecture),將接入、接出與邏輯處理相隔離,根據(jù)不同的業(yè)務(wù)操作類型合理分組,分別對(duì)待。
什么是狀態(tài)?首先,我們這里所說的狀態(tài)是一種數(shù)據(jù)。如果一個(gè)數(shù)據(jù)需要在多個(gè)服務(wù)之間共享才能完成一筆交易,那么這個(gè)數(shù)據(jù)就被稱為狀態(tài)。
依賴這個(gè)狀態(tài)的服務(wù),就是有狀態(tài)服務(wù)。否則,就是無狀態(tài)服務(wù)。
在業(yè)務(wù)上,狀態(tài)的共享是不可避免的。典型的用戶Session、現(xiàn)在新出現(xiàn)的云粘貼、網(wǎng)約車都是需要狀態(tài)在不同服務(wù)間共享的場(chǎng)景。
但在技術(shù)實(shí)現(xiàn)上,考慮到系統(tǒng)的可伸縮性,我們又需要做到無狀態(tài)化處理。
具體有四種手段:
一、請(qǐng)求方持有狀態(tài),每次調(diào)用時(shí)傳遞給服務(wù)提供方
二、粘滯+復(fù)制,這種技術(shù)并不新鮮,傳統(tǒng)的JavaEE應(yīng)用服務(wù)器集群就采用這種方式。這種方式對(duì)應(yīng)用來說簡(jiǎn)單有效,但需要中間件支持。
后面兩種手段類似,都可以稱為狀態(tài)共享
一種是,把狀態(tài)保存在分布式緩存中
另一種,把狀態(tài)保存在持久化數(shù)據(jù)庫中
區(qū)別也顯而易見,在此不做贅述。
具體使用哪種方式,要從多個(gè)維度綜合考量,這里列出了我們經(jīng)常考量的幾個(gè)維度:時(shí)間窗口、性能、可靠性、安全性。
接下來是微服務(wù)架構(gòu)下的數(shù)據(jù)一致性問題。這是一個(gè)大課題,概括的講,我們可能需要轉(zhuǎn)變思路,考慮采用柔性事務(wù),使得數(shù)據(jù)達(dá)到最終一致。
當(dāng)然,有些場(chǎng)景是必須要追求強(qiáng)一致性的,那么我們可能要在設(shè)計(jì)服務(wù)時(shí)就要考慮,是不是可以不分布。畢竟,“低耦合”是美好的,但同時(shí)還要有“高內(nèi)聚”。
保證數(shù)據(jù)最終一致性有三種模式:
1、可靠事件模式
2、補(bǔ)償模式
3、TCC模式
前面講了這些模式,從架構(gòu)角度來看挺清楚,確實(shí)也應(yīng)該這么做。但是從工程角度來看,實(shí)施起來難度其實(shí)也不小。微服務(wù)之間的調(diào)用超時(shí)、事務(wù)、異步、狀態(tài)等等,要做到代碼寫好,不出錯(cuò),恐怕也是一個(gè)門檻比較高的事情。
所以,我們強(qiáng)調(diào):用編排方式實(shí)現(xiàn)微服務(wù)的組合。
無論是配置式的編排:
還是圖形式的編排:
都可以大大降低編程復(fù)雜度,使得一些很好的架構(gòu)思想不會(huì)因?yàn)楣こ虒?shí)現(xiàn)上的復(fù)雜和高門檻,而難以落地,最終成為空談。
如果大家對(duì)編排方式實(shí)現(xiàn)微服務(wù)的組合沒有直觀的感覺,推薦大家訪問:ifttt.com,一試便知。
最后一個(gè)范式是:業(yè)務(wù)配置集中管理
記得前兩天有位群友問我,Docker啟動(dòng)時(shí),如何根據(jù)不同的參數(shù)動(dòng)態(tài)加載面向測(cè)試、生產(chǎn)環(huán)境的配置。
這個(gè)問題的答案就是業(yè)務(wù)配置集中管理。
其實(shí)技術(shù)上,業(yè)務(wù)配置集中管理沒有什么太多的難點(diǎn)。我們需要做的是有一個(gè)切實(shí)可行的步驟來逐步實(shí)現(xiàn)。
這些八個(gè)范式當(dāng)然沒有涵蓋微服務(wù)架構(gòu)中的所有方面,希望能夠在以后的實(shí)踐中總結(jié)出更多的經(jīng)驗(yàn),可以分享給大家。