使用CDC模式改造遗留系统

news2024/11/27 11:44:44

项目改造背景及挑战

在我们经历的各种遗留系统改造之旅中,使用**绞杀者模式**来改造一个巨大的单体服务,是一种被广泛采用且验证行之有效的手段,在应用传统的绞杀者模式时,通常采用逐步替换的方式,将遗留系统中某一独立的部分抽取出来进行改造,最后通过反向代理等方式,将流量倒入到新的服务中。

但是在我们的案例中,被改造的领域服务是客户的核心业务,客户强烈希望在整个改造过程中,不要对现有正在运行的系统造成影响,所以仅仅采用绞杀者模式是不够的,我们还需要采用新老并行模式来完成整个迁移改造。

当使用并行运行时,我们不是调用新旧实现的其中之一,而是同时调用二者,以允许我们比较其结果以确保它们是等效的。尽管调用了两种实现,但在任何给定的时间内,只有一个实现的结果是正确的。一般而言,在不断校验并相信我们的新实现之前,我们认为旧实现的结果是正确的。

「《Monolith To Microservices》」

通过新老并行模式,原有的系统不会受到影响,可以继续提供服务,待新的服务完成迁移并通过验证之后,再将流量迁移到新的系统。采用新老并行模式可以以增量的模式进行迁移改造,并且在出现问题的时候能够轻松回滚,确保核心业务的安全。具体介绍可以参考zalando 的工程实践。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lrrhI0dx-1668589409380)(https://insights.thoughtworks.cn/wp-content/uploads/2022/11/change-data-capture-legacy-system-1.png)]
在新老并行模式运行过程中,为了达到使新服务能够完全平行替代旧服务,需要将旧服务里新产生的变化,及时同步到新服务里来(对于遗留数据只需要一次性的迁移即可)。在各种因素以及客户业务需求的影响下,我们选择了 Event Sourcing 作为基本架构来构建我们新领域服务。Event Sourcing 是一种通过记录一系列的领域事件来完成对系统状态的持久化,对于 Event Sourcing 更加详细的介绍可以参看之前的这篇《如何正确使用Event Sourcing》。

鉴于新服务中持久化的都是一系列的领域事件,所以很难将遗留系统中产生的变化直接持久化到新服务中,最好的方式是通过调用新服务提供的 API,由新服务通过 Command 产生 Event,然后再存储到 Event Store 中,完成持久化。

同时遗留单体系统中的代码仓库已经非常庞大,并且复杂到难以修改,任何对于遗留系统的代码修改都需要经过繁复的测试和严格的 Code Review,同时也会增加交付开发人员的认知负担,并且还会给现有系统带来一定的风险。

基于以上背景,我们发现,通过修改遗留系统代码的方式来完成新老两个系统之间数据的同步代价是比较大的,而且会引入一定的风险。

以上种种限制使得我们选择了CDC(Change data capture)模式来完成我们对遗留系统数据的捕捉与迁移。

使用 CDC 模式来完成新老数据同步

什么是 CDC 模式和 Debezium

CDC 模式是一种对变化的数据进行监控并捕获,以便其他服务也能够响应这些变化的模式。对于监控数据库的变化而言,Debezium 是 CDC 模式的一个非常成熟的实现。当使用 Debezium 来连接 MySQL 时,Debezium 会读取 MySQL 的 binary log (binlog) 获取到数据库产生的变化。同时,Debezium 还是一个 Kafka connect,通过配置,能够将数据库产生的变化推送到特定的 Kakfa Topic 中。

通过 Debezium,我们便可以捕获到所有遗留系统数据库产生的变化,并将其推送到 Kafaka 特定的 Topic 中去,只要新服务能够响应这些变化,就可以将旧系统中数据产生的变化同步到新系统里去。
通过Debezium连接新旧系统
但目前而言,新服务是无法直接响应这些遗留系统数据库的变化的,原因是新服务接受的是有业务含义的 Command,而不仅仅是一些数据库的变化。但根本原因在于,我们捕获到的数据库变化只是数据库行级别的变化,缺失了特定的业务含义,所以我们也无法直接利用这些数据与我们的新服务连接起来。

将这些捕获到的数据库变化赋予业务含义,并将其转换为特定的 Command,这是我们面临的下一个挑战。

从 CDC 到 Command

要完成这个挑战,就需要深入细节了。为了方便说明,我在这里准备了一个非常简单的例子。

还是以我们非常熟悉的电商领域商品项为例,假设一个 Product 聚合根,管理着多个 Photo 聚合,它们之间的关系在遗留系统数据库中用 ERD 表示如下:
从CDC到Command
经过前期的事件风暴工作坊,我们可以得到一系列关于 Product 聚合根的领域事件:

  • Product 已增加
  • Product 的 Photo 已增加

由于我们的新服务采用了 Event Sourcing 架构,并且系统内的 Event 设计严格遵循事件风暴工作坊产出的领域事件,在新服务中,接受的是像在 Product A 下增加 Photo这样的 Comand。

有了例子之后, 我们可以将之前描述的问题更加具体一点:当收到一条消息表明 Photo 表中的数据发生了变化,应将其识别并转变为在 Product A 下增加 Photo更改 Photo A 为封面图片这样的 Command。

接下来让我们仔细分析一下 Debezium 所捕获到的变化数据的结构,继续上面的例子,如下是一个典型的 Debezium 产生的 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
  }
}

从这个消息的结构和内容可以看出,该消息里面不仅完整的展示了数据库里某项记录在变化前后的所有信息,同时还有一些附加的元信息。在这些元信息中,有两项数据最值得我们去注意。

一个是op,根据Debezium 的官方文档,这个字段表明了这次变化的变化类型,这个字段可能的值有:

  • C: 表示创建
  • U: 表示更新
  • D: 表示删除
  • R: 表示读取(如果是一个 Snapshot 的话)

通过这个字段,我们可以快速且准确的推断出当收到某条变化的消息时,遗留系统数据库的某项数据发生了怎样的变化。比如上面这个例子,我们可以推断出来,有一张新的图片被添加到某个 Product 上面。

但是到目前为止,可以将这样的消息转换为在Product A下增加Photo这样的 Command 吗?

很遗憾还不能,因为根据 Debezium 的实现以及我们的配置,每张表的更新都会被发送到不同的 Kafka Topic 中去,当收到图片被添加的消息时,还有可能是添加了一个 Product 的同时添加了这个 Product 的 Photo,所以这个行为不应该被识别为添加图片的 Command,而应该被识别为创建一个 Product的 Commnd。

为了能够准确地将数据库中发生的变化识别为 Commnd,我们需要收集并分析更多的数据,这就需要利用消息体里的另外一个字段——transaction

Transaction 字段描述了捕获到的这一次变化里关于 Transaction 的一些信息,这些信息包括了这次变化的“transactionId”,以及这个变化在这次 transaction 里的顺序。

同时,Debezium 捕获到的不仅仅是某个表中的某项记录发生变化,同时它还会捕获到每次数据库关于 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
    }
  ]
}

在每一次数据库的 transaction 开始或者结束的时候,我们都能通过 debezium 收到这样一条消息,这个消息里面,我们可以得知某一个 Transaction 的状态,id,这次 Transaction 里面有哪些表发生了变化,以及变化的数量是多少。

在这些数据中,我们最为关注的就是 Transaction 的id,可以发现在前述关于数据表变化的消息体里面也存在这个字段,通过这个字段,我们可以将某一个 Transaction 下所有产生的变化都聚合到一起,根据聚合之后的数据再来判断应该将其识别转换为哪种 Command。比如说,要是这个变化的聚合里面有一个 Product 被新增了,那么我们就可以确定的是这肯定是一个新增Product的 Command,即便这个变化的聚合里面显示出有 Photo 被新增,那也不应该被识别为成添加图片的 Command。

其实作出上面的推论还是有一个隐含前提,就是遗留系统的一些行为和操作,都是在一个 Transaction 中,如果同一个操作不在同一个 Transaction ,那么我们的推论也就无法成立。好在在我们的真实案例中,客户采用了成熟的 ORM 框架(Prisma),每一个有业务含义的行为所造成的修改都在同一个 Transaction 中并保存到数据库。在开发过程中,也需要在遗留系统的前端不断操作验证并加以单元测试才能确保我们能够准确地识别出所对应的 Command。

至此,我们所有要解决的问题都能够得到解决了,我们已经能够将遗留系统数据库与新系统的数据库之间的 gap 填平了,两者之间的通道也能够建立了。我们终于胜利了!
通过CDC完成系统迁移
基于此种解决方案,我们将整个遗留系统改造分为了三个阶段,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w9Px47hc-1668589409382)(https://insights.thoughtworks.cn/wp-content/uploads/2022/11/change-data-capture-legacy-system-5-1-1024x328.png)]

  1. 阶段一:前端对遗留系统读和写,但是对遗留系统所造成的修改都会被同步到新系统中
  2. 阶段二:前端对遗留系统进行写操作,但是对于读操作都会被引向新系统
  3. 阶段三:待对新系统做好完整的验证后,新系统就会被作为唯一可信的数据源进行读写了

更多的细节

常言道,魔鬼都在细节里,不过鉴于篇幅有限,已经无法再用文字展开更多了,只能通过时序图来介绍 CDC Procrssor 服务里更多的细节,包括如何通过Transaction来聚合 Debezium 消息以及整个消息处理流程。
CDC Procrssor 服务里更多的细节

总结

最后总结一下,因为被改造的业务是客户的核心业务,基于不影响原有业务的考虑下选择了新老并行模式来完成整个遗留系统改造。我们选择了 CDC 模式(Debezium)来将遗留系统中产生的变化同步到新服务中。在同步过程中,由数据层的变化推导出业务意图是成功的关键。在其他运用绞杀模式的改造中,如果能够在更上层的地方做分支也是一种好的思路(参考 Decorating Collaborator Pattern),这样可以更好地还原业务。最后,在使用 CDC 模式来完成遗留系统改造时,数据完整性和性能都是关键指标,在不丢失数据的情况下应越快越好。


文/Thoughtworks 张双海
原文链接:使用CDC模式改造遗留系统

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/27144.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

b站pink老师JavaScript的ES6面向对象课程中:正则表达式案例代码——表单验证

目标效果: 1.当输入的手机号,QQ号,昵称,短信验证码,登录密码,确认密码:如果符合标准,就提示正确的文字;如不符合标准,则提示不正确。 2.判断确认密码是否与登录密码相等…

落实交通强国,鄂州临空区联手蘑菇车联打造新时代内陆开放高地

临空经济与智能网联、自动驾驶能擦出什么样的火花?今年7月,鄂州花湖机场投运,标志着这个湖北省“一号工程”正式蝶变为亚洲规模最大、自动化程度行业领先的航空货运枢纽。鄂州花湖机场项目也是湖北加快建设交通强国示范区、打造新时代“祖国立…

Spring Boot配置多个日志文件记录不同类日志示例

了解如何使用多个文件追加器在Spring 引导应用程序中创建多个日志文件。了解如何使用翻转策略、归档等配置所有文件追加器,wiihlog4j2和日志配置。 1. 带登录的多个日志文件 以下文件包含 5 个记录器。我们可以根据需要创建更多的记录器。logback.xml console– …

NeRF源码运行与学习(pytorch)

神经辐射场(NeRF)是一个简单的全连接网络(权重约为5MB),经过训练,可以使用渲染损失再现单个场景的输入视图。网络直接从空间位置和观看方向(5D输入)映射到颜色和不透明度&#xff08…

翻译文本的软件有哪些?这几个翻译工具你可以试试看

文本翻译,是我们在生活中或工作中比较常见的一个需求。例如有时收到一份英文资料,没时间逐字翻译成中文,那就需要借助翻译工具来帮忙了;或者是有时需要将一些内容翻译成英文,而碰巧遇到句子不知道如何翻译,…

DDPM(Denoising Diffusion Probabilistic Models)扩散模型简述

引言 扩散模型最早是在2015年的Deep Unsupervised Learning using Nonequilibrium Thermodynamics文章中提出的,但当时扩散模型并不work,所以并没有被广泛应用。在2020年,Denoising Diffusion Probabilistic Models(简称为DDPM)的出现&#…

[附源码]java毕业设计校园闲置物品交易

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

MCE | 动物实验溶剂大讨论

在动物实验中,药物通常会以溶液 (Solution) 或混悬液 (Suspension) 的形式给药。我们需选择合适的溶剂,可以辅助超声加热措施,得到澄清的溶液或适合给药的混悬液。■ 人见人爱的生理盐水/PBS 对于水溶性很好的产品,用生理盐水 (Sa…

中学数学课程标准(教学大纲)的传承与变迁

目 录 摘 要 I Abstract II 第一章 绪论 1 1.1研究背景及意义 1 1.2研究现状 1 1.3研究内容 3 第二章 1990以来我国中学数学课程标准(教学大纲)改革回顾 4 2.1改革回顾 4 2.1.1 1990年数学教学改革的内容 4 2.1.2 2001年数学教学改革的内容 5 2.1.3 2011…

XSS(Cross-site Script,跨站脚本)漏洞笔记

起源 最早的 XSS 漏洞可追溯到 1999 年末,微软安全工程师发现一些网站遭到攻击,网站被插入了一些恶意脚本和图像标签。随后,微软对此类漏洞进行研究分析,并在 2000 年 1 月,正式使用“cross-site scripting”这个名称…

C语言-指针初阶(6)

目录 思维导图: 1. 指针是什么? 2. 指针和指针类型 2.1 指针-整数 2.2 指针的解引用 3. 野指针 3.1 野指针成因 3.2 如何规避野指针 4. 指针运算 4.1 指针-整数 4.2 指针-指针 4.3 指针的关系运算 5. 指针和数组 6. 二级指针 7. 指针数组…

50行Python代码实现自动下载小说,并打包exe直接

前言 室友喊着没有小说看,让我给他推荐几本,这能难倒我? 分分钟就用python给他把整个网站的小说都给下载下来了,不愧是我啊! 话不多说,我们直接开整! (文末送读者福利&#xff09…

python之爬虫的学习

python爬虫入门-1为什么要学习爬虫浏览器背后的秘密常用网络请求URL解析HTTP常见响应状态码相关库及其简单使用相关引用综合栗子为什么要学习爬虫 现如今,浏览器可以更方便的进行网页交互以人们适合阅读的方式展示数据;但爬虫或者网页抓取对数据的收集和…

第5章 输入/输出(I/O)管理

5.1 I/O管理概述 5.1.1 I/O设备 I/O设备的分类(按使用特性分类)I/O设备的分类(按传输速率分类)I/O设备的分类(按信息交换的单位分类) 5.1.2 I/O控制方式 有4种: 1. 程序直接控制方式2. 中…

都是同样条件的mysql select语句,为什么读到的内容却不一样?

假设当前数据库里有下面这张表。 老规矩,以下内容还是默认发生在innodb引擎的可重复读隔离级别下。 大家可以看到,线程1,同样都是读 age > 3 的数据。第一次读到1条数据,这个是原始状态。这之后线程2将id2的age字段也改成了3。…

kubelet源码 删除pod(二)

kubelet源码 删除pod(二) 本文中含有k8s的一个bug,我也正在努力提交PR,不过会不会被merge就不清楚了。 kubernetes PR地址 pod_workers.go是主要处理pod变化的文件,在1.22版本后对这个文件进行了比较大的修改。把属…

[附源码]SSM计算机毕业设计基于SSM的酒店管理系统JAVA

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

QT 字符串操作常用接口函数

目录常见字符串处理函数空白字符串处理函数查询字符串数据字符串比较字符串的转换QT版本的STLQLinkedList和QVector的区别QT提供的STL命名风格的迭代器QMap和QHash经过该简单设置可以防止msvc环境下使用qdebug打印输出时出现中文乱码的问题。 #include "learn.h" #i…

长话短说:学习网络安全自学好还是报培训班?

无论你是大学生还是在职人员,想学网络安全时,都会面临两个选择,自学或者报班。报班通常太费钱,时间又不自由;自学又不知道如何下手,担心自己坚持不下来。怎么办? 我们先分析一下自学和培训班的…

【文本分类】《融合注意力和剪裁机制的通用文本分类模型》

阅读摘要:   针对实际场景中长短文本大量的情况,提出了双通道注意力机制与长文本裁剪机制来改进文本分类模型,最终提高了精度。 参考文献:   [1] 融合注意力和剪裁机制的通用文本分类模型 参考论文信息 论文名称:《…