新聞中心
大家都在提微服務架構(gòu),微服務架構(gòu)到底是什么?它有哪些特點和設計模式?我們在打造微服務架構(gòu)過程中,這些設計模式在實戰(zhàn)當中如何應用?數(shù)據(jù)的一致性應該如何保證?今天我將針對上述疑問分享一下我的思考。

微服務架構(gòu)特點
什么是微服務架構(gòu)?看下圖的這段英文,這是Martin Fowler 在2014年提出來的,微服務架構(gòu)是一種架構(gòu)模式,既然是架構(gòu)模式,那么,它就必然需要滿足一些特點。他提到,微服務架構(gòu)是一系列小的微服務構(gòu)成的組合,那么,什么是“小的微服務”?可能每個人的理解都不一樣,大家都應該都知道SOA架構(gòu),SOA架構(gòu)的粒度是比較粗的,到底我們應該以什么樣的粒度拆分微服務?我認為,微服務架構(gòu)本質(zhì)上一個業(yè)務架構(gòu),那么對業(yè)務了解的越深刻,你的微服務拆分就越合理。
比如我們做二手交易平臺(轉(zhuǎn)轉(zhuǎn)),該平臺包括用戶體系、商品體系、交易體系以及搜索推薦體系。因為各個體系比較獨立,那么我們就可以按照各個業(yè)務模塊來拆分微服務。當然,這樣做還不夠,因為你的商品里面還有很多功能,但是大的思路是按照具體商品內(nèi)部的邏輯來進一步拆分。
第二,圍繞具體業(yè)務建模。一切脫離業(yè)務場景談微服務架構(gòu)都是耍流氓。
方法有二:首先將某一領域的模型作為獨立的業(yè)務單元:比如二手交易中的商品、訂單、用戶等;其次將業(yè)務的行為作為獨立的業(yè)務單元:比如發(fā)送郵件、單點登錄驗證、push服務。
第三,整個微服務都可以獨立地部署,因為每一個維服務Process都是獨立的,所以按照每個模塊進行獨立的部署也是很容易理解的。
第四,去中心化管理。打造去中心化管理意思就是微服務的每個模塊和開發(fā)語言、運行平臺沒有關(guān)系,開發(fā)語言可以是C++,可以是go,也可以是世界上***的語言,運行的平臺是Linux,Unix、Windows等都可以。
***一點就是輕量級通信,這點很容易理解,通信和模塊語言、平臺沒有關(guān)系。盡可能選用輕量級的通信來做這個事情,這樣實施跨平臺、跨語言的時候就很容易。
講完這些特點,我們可以看一看一個標準DEMO級的微服務架構(gòu)到底是由哪些元素組成的?如下圖,主要 包括網(wǎng)關(guān)、微服務、數(shù)據(jù)存儲、注冊中心、配置中心。
既然是DEMO級的,和實際情況下相比肯定有所差別。那么,實際案例中,我們到底應該如何做這件事情?這個例子也是最近我在做的二手交易平臺——轉(zhuǎn)轉(zhuǎn)。這里和DEMO有些不一樣的地方。前面的***層還是網(wǎng)關(guān),下面有微服務的聚合層,作用是做各種業(yè)務邏輯的處理;聚合層下面是我們的數(shù)據(jù)原子層,主要做數(shù)據(jù)訪問代理,只不過根據(jù)業(yè)務的不同垂直分開了??梢钥吹?,網(wǎng)關(guān)、數(shù)據(jù)層,注冊中心、配置中心都有,只不過在業(yè)務處理部分分成兩層:一層是原子層,也就是整個數(shù)據(jù)訪問的代理層,提供了用戶的接口;另外一層就是上層的業(yè)務聚合層。
架構(gòu)設計模式及實踐案例
上面我大概講了下微服務的一些特點以及DEMO級的微服務包括哪些部分以及實際案例中我們的設架構(gòu)設計模式。那么,我們?yōu)槭裁匆捎眠@種模式去做?除了這種架構(gòu)模式之外還有哪些其它的架構(gòu)模式?這里,模式還是非常多的,我會重點講這幾點:鏈式設計模式、聚合器設計模式和異步共享模式。
首先我們來說下鏈式設計模式,在這種模式下,APP前端請求首先要經(jīng)過網(wǎng)關(guān)層,接下來連續(xù)調(diào)用兩個微服務,調(diào)了微服務1之后還要調(diào)微服務2。為什么叫做鏈式呢?因為在調(diào)用過來以后先到微服務1,然后再同步地調(diào)用微服務2,微服務2會做一些處理,處理以后微服務2才會反饋給微服務1,微服務1再反饋給Gateway,***反饋到APP。在實際業(yè)務場景中,涉及到交易和訂單的業(yè)務場景都會用到這種模式。
接下來是聚合器設計模式,APP前端一個調(diào)用請求經(jīng)過Gateway,到達聚合層,需要調(diào)用三個微服務,聚合層將三個微服務的返回結(jié)果做一些聚合處理,比如可以進行一些排序或者去重,聚合之后再反饋到Gateway和APP前端,這是一個典型的聚合器設計模式。
第三種模式是數(shù)據(jù)共享模式,這種模式相對比較簡單,比如APP經(jīng)過微服務網(wǎng)關(guān),接下來調(diào)用微服務1和微服務2,理想情況下微服務1和微服務2都有自己獨立的DB,但是有些情況下由于微服務1和微服務2的請求量和存儲量較小,從資源利用率的角度來講,這兩個微服務的DB是共享的,因此這種就是數(shù)據(jù)的共享模式。
***一種是異步消息設計模式,不管是鏈式設計、聚合器模式還是共享數(shù)據(jù)模式,架構(gòu)模式都是同步模式。也就是說我的一個請求發(fā)出去必須等到每個環(huán)節(jié)都處理完才會給客戶端。如果請求不需要關(guān)注處理結(jié)果,這時候可以異步來實施。APP更新請求經(jīng)過微服務網(wǎng)關(guān),持久化到MQ,寫入MQ成功后馬上Response給APP客戶端,之后微服務根據(jù)需要從MQ里面訂閱更新消息進行異步處理,我們?yōu)榱颂岣咄掏铝恳矔捎眠@種模式。
我從百度到轉(zhuǎn)轉(zhuǎn)這幾年經(jīng)歷了很多業(yè)務場景,使用的無非就是聚合器、異步和數(shù)據(jù)共享的數(shù)據(jù)模式,特別是前面兩個用得特別多,下面我們來看一些例子。
接下來我們看個例子,這是我們在2015年做的一個二手交易平臺(轉(zhuǎn)轉(zhuǎn)),這個二手交易平臺包括商品、分類搜索、關(guān)鍵詞搜索、商品推薦等功能。一個用戶請求過來,先經(jīng)過網(wǎng)關(guān),網(wǎng)關(guān)下面就是我們的聚合層,聚合層再去調(diào)用商品、交易、推薦以及搜索相關(guān)的,最終在聚合層把各個微服務原子層的結(jié)果匯總起來Response給到客戶端。具體如下圖所示:
異步消息模式的這個案例比較早了,當時我們做了Feed 流,類似現(xiàn)在的微信朋友圈,這是我在百度做的事情。當時,我們采用的架構(gòu)模式是異步架構(gòu)模式。前面是我們的APP,經(jīng)過了網(wǎng)關(guān),到達異步提交層,可以認為是持久化功能的MQ。用戶請求經(jīng)過網(wǎng)關(guān)到消息異步提交層后就返回了,業(yè)務處理部分從MQ里面讀取數(shù)據(jù)再進行異步處理。這個時候吞吐量會增加,但是會帶來一定的困惑。比如這個時候我發(fā)了一條Feed,用戶再一查就直接到數(shù)據(jù)庫里面查,可能異步提交消息隊列有延遲,查不到,用戶就困惑了,這個問題怎么解決?我們就想能不能在前端幫我們做一些事情?比如提交了MQ返回Response 200以后,前段配合插入這條Feed。用戶再次刷新時候我相信已經(jīng)是好幾秒以后的事情了,即使有延遲,這個消息早就被你的業(yè)務處理完了。當然,我們這里是有特定場景的,社區(qū)時候可以這樣去做,但是涉及到和金融相關(guān)的場景肯定不會這么去做。
數(shù)據(jù)一致性實踐
微服務模塊比較分散、數(shù)據(jù)也比較分散,整個系統(tǒng)復雜性非常高,如何進行數(shù)據(jù)一致性實踐?在一個單體模塊里面可以做Local Transaction,但是在微服務體系里面就不奏效。雖然難解決,但是不能不解決,不解決的話微服務架構(gòu)就很難實施。我們知道微服務中做強一致性性的事情是非常難的,今天分享的更多的是解決最終一致性。因為在微服務下基于不同的數(shù)據(jù)庫,Local Transaction是不可用的。大家在在分布式事務里面一定聽說過兩階段提交和三階段提交,這種場景其實在微服務架構(gòu)里面也行不通,原因是因為它本質(zhì)上是同步的模式,同步的模式之下做數(shù)據(jù)一致性吞吐量降低的非常多。
我們的業(yè)務場景無非是兩種:***種是異步調(diào)用,就是一個請求過來就寫到消息隊列里面就行,這種模式相對簡單。今天主要講下同步調(diào)用的場景之下怎么打造數(shù)據(jù)的最終一致性。既然是同步調(diào)用場景,并且不能降低業(yè)務系統(tǒng)的吞吐量,那么應該怎么做呢?建立一個異步的分布式事務,業(yè)務調(diào)用失敗后,通過異步方式來補償業(yè)務。我們的想法是能不能在整個業(yè)務邏輯層實現(xiàn)分布式事務語義策略?如何實現(xiàn),無非有兩種,***是在調(diào)正常請求的時候要記錄業(yè)務調(diào)用鏈(調(diào)用正常接口的完整參數(shù)),第二是異常時沿調(diào)用鏈反向補償。
基于這個思路,我們架構(gòu)設計上的關(guān)鍵點有三,***是基于補償機制,第二是記錄調(diào)用鏈,第三是提供冪等補償接口。架構(gòu)層面,看下圖,右邊是聚合器架構(gòu)設計模式,左邊是異步補償服務。
首先需要在聚合層引入一個Proxy。首先基于方法,在方法名加注解標注補償方法名,比如:- @Compensable(cancelMethod=“cancelRecord”)
另外,聚合層在調(diào)用原子層之前,通過代理記錄當前調(diào)用請求參數(shù)。如果業(yè)務正常,調(diào)用結(jié)束后,當前方法的調(diào)用記錄存檔或刪除,如果業(yè)務異常,查詢調(diào)用鏈回滾。
原子層我們做了哪些事情呢?主要是兩方面,***是提供正常的原子接口,其次是提供補償冪等接口。
分布式事務關(guān)鍵是兩個表(如上圖),***是事務組表,假設A->B->C三個請求是一個事務,首先針對ABC生成一個事務的ID,寫在這個表里面,并且會記錄這個事務的狀態(tài),默認的情況下正常的,執(zhí)行失敗以后我們再把狀態(tài)由1(正常)變成2(異常);第二個表是事務調(diào)用組表,主要記錄事務組內(nèi)的每一次調(diào)用以及相關(guān)參數(shù),所以調(diào)用原子層之前需要記錄一下請求參數(shù)。如果失敗的話我們需要把這個事務的狀態(tài)由1變成2;第三,一旦狀態(tài)從1變成2就執(zhí)行補償服務。這是我們的補償邏輯,就是不斷地掃描這個事務所處的表,比如一秒鐘掃一次事務組表,看一看這個表里面有沒有狀態(tài)為2的,需要執(zhí)行補償?shù)姆铡_@個思路對業(yè)務的侵入比較小。
具體看下我們實際的例子,比如二手交易平臺里面創(chuàng)建訂單事務組的正常流程,從鎖庫存到減紅包再到創(chuàng)建訂單,創(chuàng)建事務組完畢之后開始調(diào)用業(yè)務,首先Proxy記錄鎖庫存調(diào)用的參數(shù),之后開始鎖庫存服務調(diào)用,成功后之后又開始減紅包和創(chuàng)建訂單過程,如果都成功了直接返回。
再看一下異常的流程,前面幾步都是一樣的,只是在調(diào)紅包服務、Proxy創(chuàng)建紅包的時候如果失敗了就會拋出異常,業(yè)務正常返回,聚合層Proxy需要把事務組的狀態(tài)由1改成2,這個時候由左邊的補償服務異步地補償調(diào)用。
作者簡介:孫玄,58集團技術(shù)委員會主席,本文來自于作者在CCTC 2017上的演講整理。
當前題目:實施微服務架構(gòu)的關(guān)鍵技術(shù)
分享地址:http://www.dlmjj.cn/article/dppejig.html


咨詢
建站咨詢
