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