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