網(wǎng)上有很多關于pos機改造,使用CDC模式改造遺留系統(tǒng)的知識,也有很多人為大家解答關于pos機改造的問題,今天pos機之家(m.afbey.com)為大家整理了關于這方面的知識,讓我們一起來看下吧!
本文目錄一覽:
1、pos機改造
pos機改造
項目改造背景及挑戰(zhàn)在我們經(jīng)歷的各種遺留系統(tǒng)改造之旅中,使用絞殺者模式來改造一個巨大的單體服務,是一種被廣泛采用且驗證行之有效的手段,在應用傳統(tǒng)的絞殺者模式時,通常采用逐步替換的方式,將遺留系統(tǒng)中某一獨立的部分抽取出來進行改造,最后通過反向代理等方式,將流量倒入到新的服務中。
但是在我們的案例中,被改造的領域服務是客戶的核心業(yè)務,客戶強烈希望在整個改造過程中,不要對現(xiàn)有正在運行的系統(tǒng)造成影響,所以僅僅采用絞殺者模式是不夠的,我們還需要采用新老并行模式來完成整個遷移改造。
當使用并行運行時,我們不是調(diào)用新舊實現(xiàn)的其中之一,而是同時調(diào)用二者,以允許我們比較其結果以確保它們是等效的。盡管調(diào)用了兩種實現(xiàn),但在任何給定的時間內(nèi),只有一個實現(xiàn)的結果是正確的。一般而言,在不斷校驗并相信我們的新實現(xiàn)之前,我們認為舊實現(xiàn)的結果是正確的。
「《Monolith To Microservices》」
通過新老并行模式,原有的系統(tǒng)不會受到影響,可以繼續(xù)提供服務,待新的服務完成遷移并通過驗證之后,再將流量遷移到新的系統(tǒng)。采用新老并行模式可以以增量的模式進行遷移改造,并且在出現(xiàn)問題的時候能夠輕松回滾,確保核心業(yè)務的安全。具體介紹可以參考zalando 的工程實踐。
在新老并行模式運行過程中,為了達到使新服務能夠完全平行替代舊服務,需要將舊服務里新產(chǎn)生的變化,及時同步到新服務里來(對于遺留數(shù)據(jù)只需要一次性的遷移即可)。在各種因素以及客戶業(yè)務需求的影響下,我們選擇了 Event Sourcing 作為基本架構來構建我們新領域服務。Event Sourcing 是一種通過記錄一系列的領域事件來完成對系統(tǒng)狀態(tài)的持久化,對于 Event Sourcing 更加詳細的介紹可以參看之前的這篇《如何正確使用Event Sourcing》。
鑒于新服務中持久化的都是一系列的領域事件,所以很難將遺留系統(tǒng)中產(chǎn)生的變化直接持久化到新服務中,最好的方式是通過調(diào)用新服務提供的 API,由新服務通過 Command 產(chǎn)生 Event,然后再存儲到 Event Store 中,完成持久化。
同時遺留單體系統(tǒng)中的代碼倉庫已經(jīng)非常龐大,并且復雜到難以修改,任何對于遺留系統(tǒng)的代碼修改都需要經(jīng)過繁復的測試和嚴格的 Code Review,同時也會增加交付開發(fā)人員的認知負擔,并且還會給現(xiàn)有系統(tǒng)帶來一定的風險。
基于以上背景,我們發(fā)現(xiàn),通過修改遺留系統(tǒng)代碼的方式來完成新老兩個系統(tǒng)之間數(shù)據(jù)的同步代價是比較大的,而且會引入一定的風險。
以上種種限制使得我們選擇了CDC(Change data capture)模式來完成我們對遺留系統(tǒng)數(shù)據(jù)的捕捉與遷移。
使用 CDC 模式來完成新老數(shù)據(jù)同步什么是 CDC 模式和 DebeziumCDC 模式是一種對變化的數(shù)據(jù)進行監(jiān)控并捕獲,以便其他服務也能夠響應這些變化的模式。對于監(jiān)控數(shù)據(jù)庫的變化而言,Debezium 是 CDC 模式的一個非常成熟的實現(xiàn)。當使用 Debezium 來連接 MySQL 時,Debezium 會讀取 MySQL 的 binary log (binlog) 獲取到數(shù)據(jù)庫產(chǎn)生的變化。同時,Debezium 還是一個 Kafka connect,通過配置,能夠?qū)?shù)據(jù)庫產(chǎn)生的變化推送到特定的 Kakfa Topic 中。
通過 Debezium,我們便可以捕獲到所有遺留系統(tǒng)數(shù)據(jù)庫產(chǎn)生的變化,并將其推送到 Kafaka 特定的 Topic 中去,只要新服務能夠響應這些變化,就可以將舊系統(tǒng)中數(shù)據(jù)產(chǎn)生的變化同步到新系統(tǒng)里去。
但目前而言,新服務是無法直接響應這些遺留系統(tǒng)數(shù)據(jù)庫的變化的,原因是新服務接受的是有業(yè)務含義的 Command,而不僅僅是一些數(shù)據(jù)庫的變化。但根本原因在于,我們捕獲到的數(shù)據(jù)庫變化只是數(shù)據(jù)庫行級別的變化,缺失了特定的業(yè)務含義,所以我們也無法直接利用這些數(shù)據(jù)與我們的新服務連接起來。
將這些捕獲到的數(shù)據(jù)庫變化賦予業(yè)務含義,并將其轉(zhuǎn)換為特定的 Command,這是我們面臨的下一個挑戰(zhàn)。
從 CDC 到 Command要完成這個挑戰(zhàn),就需要深入細節(jié)了。為了方便說明,我在這里準備了一個非常簡單的例子。
還是以我們非常熟悉的電商領域商品項為例,假設一個 Product 聚合根,管理著多個 Photo 聚合,它們之間的關系在遺留系統(tǒng)數(shù)據(jù)庫中用 ERD 表示如下:
經(jīng)過前期的事件風暴工作坊,我們可以得到一系列關于 Product 聚合根的領域事件:
Product 已增加Product 的 Photo 已增加…由于我們的新服務采用了 Event Sourcing 架構,并且系統(tǒng)內(nèi)的 Event 設計嚴格遵循事件風暴工作坊產(chǎn)出的領域事件,在新服務中,接受的是像在 Product A 下增加 Photo這樣的 Comand。
有了例子之后, 我們可以將之前描述的問題更加具體一點:當收到一條消息表明 Photo 表中的數(shù)據(jù)發(fā)生了變化,應將其識別并轉(zhuǎn)變?yōu)樵?Product A 下增加 Photo或更改 Photo A 為封面圖片這樣的 Command。
接下來讓我們仔細分析一下 Debezium 所捕獲到的變化數(shù)據(jù)的結構,繼續(xù)上面的例子,如下是一個典型的 Debezium 產(chǎn)生的 Kafka 消息的 payload 結構:
{ "before": null, "after": { "id": "61CFF6E6-A7AA-43BB-8D6D-A8676CFF59AE", "productId": "E78E3F5A-2275-4C2D-AA6E-1F0282E6CC08", "filePath": "48F3AE46-D6A5-4F14-8FE3-7B82B7EB6537", "order": 0, "isCover": true, } "source": {...some source meta infORMation}, "op": "c", "ts_ms": 1631690854515, "transaction": { "Id": "file=mysql-bin-changelog.000010,pos=40908353", "total_order": 1, "data_collection_order": 1 }}
從這個消息的結構和內(nèi)容可以看出,該消息里面不僅完整的展示了數(shù)據(jù)庫里某項記錄在變化前后的所有信息,同時還有一些附加的元信息。在這些元信息中,有兩項數(shù)據(jù)最值得我們?nèi)プ⒁狻?/p>
一個是op,根據(jù)Debezium 的官方文檔,這個字段表明了這次變化的變化類型,這個字段可能的值有:
C: 表示創(chuàng)建U: 表示更新D: 表示刪除R: 表示讀?。ㄈ绻且粋€ Snapshot 的話)通過這個字段,我們可以快速且準確的推斷出當收到某條變化的消息時,遺留系統(tǒng)數(shù)據(jù)庫的某項數(shù)據(jù)發(fā)生了怎樣的變化。比如上面這個例子,我們可以推斷出來,有一張新的圖片被添加到某個 Product 上面。
但是到目前為止,可以將這樣的消息轉(zhuǎn)換為在Product A下增加Photo這樣的 Command 嗎?
很遺憾還不能,因為根據(jù) Debezium 的實現(xiàn)以及我們的配置,每張表的更新都會被發(fā)送到不同的 Kafka Topic 中去,當收到圖片被添加的消息時,還有可能是添加了一個 Product 的同時添加了這個 Product 的 Photo,所以這個行為不應該被識別為添加圖片的 Command,而應該被識別為創(chuàng)建一個 Product的 Commnd。
為了能夠準確地將數(shù)據(jù)庫中發(fā)生的變化識別為 Commnd,我們需要收集并分析更多的數(shù)據(jù),這就需要利用消息體里的另外一個字段——transaction。
Transaction 字段描述了捕獲到的這一次變化里關于 Transaction 的一些信息,這些信息包括了這次變化的“transactionId”,以及這個變化在這次 transaction 里的順序。
同時,Debezium 捕獲到的不僅僅是某個表中的某項記錄發(fā)生變化,同時它還會捕獲到每次數(shù)據(jù)庫關于 Transaction 的一些原始信息,消息格式如下:
{ "status": "END", "id": "file=mysql-bin-changelog.000010,pos=40908353", "event_count": 2, "data_collections": [ { "data_collection": "product-service@photo", "event_count": 1 }, { "data_collection": "product-service@product", "event_count": 1 } ]}
在每一次數(shù)據(jù)庫的 transaction 開始或者結束的時候,我們都能通過 debezium 收到這樣一條消息,這個消息里面,我們可以得知某一個 Transaction 的狀態(tài),id,這次 Transaction 里面有哪些表發(fā)生了變化,以及變化的數(shù)量是多少。
在這些數(shù)據(jù)中,我們最為關注的就是 Transaction 的id,可以發(fā)現(xiàn)在前述關于數(shù)據(jù)表變化的消息體里面也存在這個字段,通過這個字段,我們可以將某一個 Transaction 下所有產(chǎn)生的變化都聚合到一起,根據(jù)聚合之后的數(shù)據(jù)再來判斷應該將其識別轉(zhuǎn)換為哪種 Command。比如說,要是這個變化的聚合里面有一個 Product 被新增了,那么我們就可以確定的是這肯定是一個新增Product的 Command,即便這個變化的聚合里面顯示出有 Photo 被新增,那也不應該被識別為成添加圖片的 Command。
其實作出上面的推論還是有一個隱含前提,就是遺留系統(tǒng)的一些行為和操作,都是在一個 Transaction 中,如果同一個操作不在同一個 Transaction ,那么我們的推論也就無法成立。好在在我們的真實案例中,客戶采用了成熟的 ORM 框架(Prisma),每一個有業(yè)務含義的行為所造成的修改都在同一個 Transaction 中并保存到數(shù)據(jù)庫。在開發(fā)過程中,也需要在遺留系統(tǒng)的前端不斷操作驗證并加以單元測試才能確保我們能夠準確地識別出所對應的 Command。
至此,我們所有要解決的問題都能夠得到解決了,我們已經(jīng)能夠?qū)⑦z留系統(tǒng)數(shù)據(jù)庫與新系統(tǒng)的數(shù)據(jù)庫之間的 gap 填平了,兩者之間的通道也能夠建立了。我們終于勝利了!
基于此種解決方案,我們將整個遺留系統(tǒng)改造分為了三個階段,
階段一:前端對遺留系統(tǒng)讀和寫,但是對遺留系統(tǒng)所造成的修改都會被同步到新系統(tǒng)中階段二:前端對遺留系統(tǒng)進行寫操作,但是對于讀操作都會被引向新系統(tǒng)階段三:待對新系統(tǒng)做好完整的驗證后,新系統(tǒng)就會被作為唯一可信的數(shù)據(jù)源進行讀寫了更多的細節(jié)常言道,魔鬼都在細節(jié)里,不過鑒于篇幅有限,已經(jīng)無法再用文字展開更多了,只能通過時序圖來介紹 CDC Procrssor 服務里更多的細節(jié),包括如何通過Transaction來聚合 Debezium 消息以及整個消息處理流程。
總結最后總結一下,因為被改造的業(yè)務是客戶的核心業(yè)務,基于不影響原有業(yè)務的考慮下選擇了新老并行模式來完成整個遺留系統(tǒng)改造。我們選擇了 CDC 模式(Debezium)來將遺留系統(tǒng)中產(chǎn)生的變化同步到新服務中。在同步過程中,由數(shù)據(jù)層的變化推導出業(yè)務意圖是成功的關鍵。在其他運用絞殺模式的改造中,如果能夠在更上層的地方做分支也是一種好的思路(參考 Decorating Collaborator Pattern),這樣可以更好地還原業(yè)務。最后,在使用 CDC 模式來完成遺留系統(tǒng)改造時,數(shù)據(jù)完整性和性能都是關鍵指標,在不丟失數(shù)據(jù)的情況下應越快越好。
文/ Thoughtworks 張雙海
更多精彩洞見,請關注公眾號Thoughtworks洞見
以上就是關于pos機改造,使用CDC模式改造遺留系統(tǒng)的知識,后面我們會繼續(xù)為大家整理關于pos機改造的知識,希望能夠幫助到大家!
