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