如何遷移到微服務和事件溯源EventSourcing

banq發表於2018-03-06
這是一篇提供如何從單體大型應用遷移到微服務+事件溯源的指導性文章,文章提供了六條建議,主要是確定微服務邊界,將事件作為首要設計,將系統從過去面向介面的耦合變成面向事件資料的耦合,從而大大地增加微服務的獨立性和靈活性,同時為效能的彈性擴充套件提供了可能。所謂面向介面耦合,就是你事先設計幾個介面,定義其中的方法,讓耦合雙方共同圍繞這些介面進行實現和呼叫,如果這是一種X座標思維,那麼你可以轉換到Y座標思維,將這幾種介面型別設計為幾種事件型別,讓使用者和被使用者透過釋出事件和獲取事件方式進行耦合,這種耦合方式比介面耦合更加松耦合,而且可擴充套件。

下面是Pedro Costa這篇原文的大意翻譯:

像'微服務'和'事件溯源Event-Sourcing'這樣的流行語對軟體行業來說絕對不是新鮮事物,因為它們已被用於解決大多數大型企業應用程式面臨的共同問題。現在已經有更多關於它們的累積知識,並且越來越多的應用程式正在使用這種模式構建,因此更容易得出一些結論。在此,我將列舉一些關鍵考慮因素,同時思考如何將大型單體應用程式遷移到微服務和事件溯源架構。我還將提供一些指導方針,讓您在遷移過程中遵循這些指導原則,同時對我自己使用的庫和工具(如Apache Kafka和Protobuf)分享一些心得,以便在最近開展的一些專案中是如何實現它們的。

微服務
如果讀者不知道關於微服務架構,可以參考Martin Fowler的話:

簡而言之,微服務架構風格是一種將單個應用程式(單體)分解為一套小型服務開發的方法,每個服務都在自己的程式中執行,並透過輕量級機制(...)進行通訊。這些服務是圍繞業務功能構建的,可以透過全自動部署機制獨立部署,(...)可以用不同的程式語言編寫,並使用不同的資料儲存技術。

架構師喜歡它們,因為它們是將大型緊密耦合的軟體巨石分離成小型、獨立和鬆散耦合的服務的有效方式。這些獨立服務應該具有有限的範圍,明確的責任,並有權擁有自己的資料模型。

這套特性強化了模組化和責任分離,提高了獨立開發團隊的自主性,因為他們將從事具有明確邊界的任務,解耦依賴關係並減少與其他團隊工作的重疊。這種自主權允許開發團隊選擇最適合他們手中每個問題需求的工具。DevOps團隊也應該喜歡這種方法,因為微服務應該易於部署並執行在自己的容器中。

我還應該提到可伸縮性和容錯性方面的好處 - 執行在孤立程式上的微服務透過僅啟動服務的新例項而在負載高峰期更容易擴充套件; 整個系統變得更具彈性,因為單點故障減少:如果某項服務降級或失敗,其他服務能夠獨立執行工作。

事件溯源

如何遷移到微服務和事件溯源EventSourcing

事件溯源是一個完全不同的故事。它的想法是以不可變事件的形式表示每個應用程式的狀態轉換。事件隨後以日誌或日記的形式被儲存,因為它們發生(也可以參考“事件儲存”) 。它們也可以被無限期地查詢和儲存,旨在表示整個應用程式的狀態隨著時間的推移而發展。

為什麼要把微服務和事件採購放在一起?
請記住,透過選擇微服務,我們的目標是構建能夠動態擴充套件和適應傳入流量的系統,最好是伸縮規模,這意味著更好更高效地利用可用資源,並最終減少我們所需資源的數量系統。

要建立Reactive響應反應式的應用,這不是依靠傳統的阻塞請求/響應方式,而是一種立即響應的方式,它增加了整個系統的響應能力的使用可用資源的更好的有效途徑。與其阻塞並等待計算完成,應用程式將忙於使用可用資源非同步地處理使用者請求,同時在不會在主執行緒上執行繁重的任務而造成執行緒阻塞。

正是在這裡,事件才發揮作用:事件是思考反應系統的核心問題,它代表了在微服務之間實現非同步通訊的有效方式,並代替了傳統的同步/阻塞模型,如JSON over REST / HTTP或其他對等體對特定協議。代表了每個時刻的應用程式最新狀態,不同的微服務可以使用事件來構建其本地狀態和資料模型。微服務可以在短期內使用Pub /Sub模式來僅使用與其範圍相關的事件。

Konrad Malawski的報告:為什麼是反應性的? 對於Reactive應用程式如何成為實現可伸縮性和響應能力以及分散式系統複雜性的相關方式而言是非常好的選擇。

但是,真的,你真的應該遷移嗎?
到目前為止,我們已經將微服務和事件確定為擁抱模組化、反應式和非阻塞應用程式的模式; 現在我們將討論如何將事件儲存、查詢並廣播到感興趣的服務。但是,也許現在是時候回答你的問題了:我是否應該真正轉向微服務和事件採購架構?答案是:這取決於!

確定應用程式是否能夠充分受益於這一複雜體系結構的一個很好的經驗法則是檢視其目標平臺 - 您的應用程式是否有要求在不受您控制的異構客戶機特定平臺上執行?您的系統是否需要舊式“下一個,下一個,下一個型別”安裝器?

如果您對上述問題回答“ 是”,那麼您的系統可能不會從微服務體系結構的全部潛力中受益,因為您無法監控平臺的執行狀況或動態調整資源以應對系統負載。按需生成新的微服務例項以響應高流量峰值也不可行,因為您無法控制可用資源和可用性統計資訊來有效執行操作。

另一個重要問題是:您的應用程式是否具有很高的效能要求,或者在很長一段時間內每秒會有數千次使用者請求?

如果這次你回答否,你可能不會從非同步中受益,因為你可以構建傳統的同步和單使用者的用例。

同樣重要的是要注意,如果您正在討論的是帶有大型和舊式程式碼堆疊的應用程式,而這些應用程式並非圍繞強大的功能責任明確分離的固體模組化架構構建,而是依靠通用API進行內部通訊,對於你和你的團隊來說,重新構建它可能會更有效,而不是花費大量的時間來分解它,並改變它的內部通訊流。對您的系統是否值得采取行動做出冷淡和公正的判斷很重要,因為遷移過程肯定會花費您大量的時間和資源。

將單體移植到微服務中
這裡的想法是執行增量遷移,以使其儘可能平滑和無痛。試圖一次性做到這一點將是一個自殺式的使命 - 一方面,沒有人會以完美的判斷力樂於處理這種移民意味著的“大爆炸”整合,另一方面,沒有經理人將推遲2到3個月的功能路線圖,以便開發團隊可以顛倒專案的體系結構。

我推薦的方法對我來說很合適,就是避免重寫現有的生產程式碼,至少在發現實際問題或釋出新功能之前。嘗試開始圍繞現有系統構建新的服務,採用更具反應性的開發方式,只有在需要時才重寫舊的內部結構。

如何遷移到微服務和事件溯源EventSourcing


1 - 確定主要用途
第一個也許是你手中最難的任務是定義服務的邊界。記住:一個 微服務應該有一個定義明確的責任,應該對映到一個或多或少簡單的需求用例。

正如您現在應該知道的那樣,服務應儘可能與其對等體分離並自治,並擁有自己的永續性模組。這種方法可以更有效地處理資料模型,因為每個服務只會管理和儲存它真正需要的狀態,並且以一種非常適合其使用情況的格式 - 更多的模組化和責任分離點。

2 - 解耦內部元件並重新定義其資料模型
既然您已經明確了主要需求用例並定義了微服務的邊界,那麼使用這個設計階段來檢視您的應用程式,並反過來確定其當前軟體層可以如何對映新定義的服務。不要同時期望這是一件容易的事,因為通常整體式應用程式不是面向用例的:大多數用例將跨越多個抽象層並執行多次資料轉換。

在微服務方法中,我們旨在實現自頂向下的隔離,每個用例都儘可能少地與其他服務進行互動,並且儘可能以最少的資料轉換。要獲得高響應吞吐量,對每項特定任務使用最佳化的資料模型至關重要。舉例來說,也許這將是有幫助的JSON支援的資料庫,像Elasticsearch用於對於只生產JSON響應,或可能考慮服務基於圖的資料庫像Neo4j用於只執行路由計算的服務。

3 - 設計清潔和通用的API
現在是時候透過設計乾淨和演變的API來規範服務間通訊協議。記得事件嗎?現在你應該實現它們並選擇一種技術。在這個設計階段,您將把所有應用程式的狀態轉換編碼為小型的面向實體的事件。這些事件將在系統內部流動,並被幾種不同的服務消費使用以建立本地狀態,因此為事件定義選擇一種可靠的技術至關重要,它具有經過驗證的快速處理和良好的編碼/解碼效能結果。

我一直在使用Google的Protobuf獲得很好的體驗,但是還有其他幾種類似技術適合像Thrift或Avro這樣的任務。檔案大小和序列化/反序列化時間方面,Protobuf和Thrift非常類似,比普通的JSON或XML表現得更好。Avro對於大型物件表現不錯,但對於我們的小型事件則不太合適。

Protobuf是一個非常棒的協議:Google在其核心應用程式中使用它,它有很好的文件記錄並被業界廣泛採用。它也被證明非常適合透過支援可選欄位來允許API的演變。這使得不同版本的相同API可以被不同的服務使用,只要遵循將'non-required'新增到新欄位的方法,讓應用程式層實現預設邏輯,以避免這些欄位缺失。

4 - 新增事件代理
如果事件在您的應用程式中被視為一等公民,您最好找到一種有效的方式來充分利用它們 - 您選擇的事件代理對整體效能至關重要,因為它將成為實現不同微服務的核心元件相互溝通。

對於Apache Kafka我有很好的評價 - 它已經被證明是一個可靠和高效的平臺,用於儲存和傳送每秒數千個事件。你可以找到關於卡夫卡的複製和容錯模型的更多細節在這裡

Kafka既可以用作訊息代理,也可以作為事件的持久儲存,因為它可以無限期地儲存在磁碟上,可以隨時從主題topic中消費使用它們(但不會刪除)。

你的主題topic將是不可改變的,並且有序的事件將以結構化的形式增長。然後,事件被分配offset變數以唯一地識別它們在主題中位置  -卡夫卡可以管理偏移量本身,很容易提供“最多一次”或“至少一次”的傳遞語義,當一個事件消費者加入一個主題,允許微服務從任何時間點開始消費事件。如果最後將事件偏移量在使用者成功完成時事務性地保留在服務的本地儲存中,那麼可以很容易地使用該偏移量來實現“恰好一次”事件傳遞語義。

5 - 仔細設計事件主題

[img index=1]

上圖是比特幣交易採取的(並且非常簡化)的微服務+事件採購event-sourcing架構。面向實體的主題的亮點是面向用例的微服務

這裡的主題是一個重要的抽象,對它們建模的不同方式將會產生所有的不同點:它們就像事件的儲存庫一樣,使用者可以根據自己的意願定義多少。已經證明對我有效的策略是為每種實體型別的事件使用不同的主題,使它們面向實體而不是面向用例。 這樣,我總是知道某個Topic只代表特定資料模型實體的狀態轉換,而不是將它成為用例的一部分。這種方法使事件消費大大簡化,使服務僅消費他們感興趣的狀態轉換,而且這通常是與不同微服務有關的唯一的通用資訊。

這個概念在上圖 - 主題中儲存了第一類實體,這些實體是一些事件實體,例如Logins,Orders或Fills; 微服務處理簡單的用例,如下訂單,顯示訂單簿,顯示價格圖表等。我們還可以看到無狀態服務根本不共享資料庫:Trading History服務使用更多的面向JSON的永續性模型,對於User Balance服務來說,關聯式資料庫就足夠了。人們還應該注意到,像Price Chart或Order Book這樣的不同的有狀態服務透過消費Fills Topic和其他地方的事件來獨特地建立其本地狀態。

對於確實需要不同服務之間的通訊的更復雜的用例,必須認識到完成用例的責任 - 用例是分散的,並且只有當涉及的所有服務都確認他們的任務已成功完成時才會完成,否則整個用例必須失敗並且必須採取糾正措施來回滾任何無效的本地狀態。這實際上是一種在微服務架構中使用的稱為“ Saga”的非常常見的模式。

6 - 將碎片粘合在一起
現在您已經完成了所有建議的(重新)設計階段,現在是部署新基礎架構並實施新的簡單功能的時候了。請記住,一次只能提供一項服務,給自己留下實驗訊息代理的空間,並收集有關新的反應式系統的效能統計資訊。不要忘記適當地衡量你的效能和響應時間,因為在適當的平臺微調之前,有時會引入一些意想不到的延遲開銷。

最後的想法
請記住,微服務應用程式是分散式系統,分散式系統本質上是複雜的。話雖如此,不要指望遷移到微服務和事件採購是一件容易的事情,也不要期望在第一次嘗試之後就能立刻獲得它。你肯定會犯錯誤,但我相信如果你按照我在這裡描述的主要指導原則,他們會大大減少: 執行增量遷移 ; 為未來的改進留出空間;


Migrating to Microservices and Event-Sourcing: the



[該貼被banq於2018-03-07 08:07修改過]

相關文章