本文深入讨论了事件来源、命令查询责任隔离(CQRS)、变更数据捕获(CDC)和发件箱模式等领域。这些解决方案的价值将得到迫切需要的澄清。此外,将详细解释两种不同的设计的优点/缺点。

那么为什么所有这些解决方案都很重要呢?它们很重要,因为许多团队正在构建微服务,并将数据分布到多个数据存储中。一个微服务系统可能涉及关系数据库、对象存储、内存缓存,甚至数据的可搜索索引。开云体育电动老虎机数据可能很快丢失、不同步,甚至损坏,因此对关键任务系统造成灾难性后果。

对于许多组织来说,帮助避免这些严重问题的解决方案至关重要。不幸的是,许多重要的解决方案都有些难以理解;事件源、CQRS、CDC和发件箱也不例外。请将这些解决方案视为学习和理解它们如何应用于您的特定用例的机会。

正如您将在本文最后发现的那样,我将提出这四种解决方案中的三种具有很高的价值,而另一种解决方案不应该使用,除非在最罕见的情况下。本文给出的建议应该根据您的具体需求进行评估,因为在某些情况下,这四种解决方案都不太合适。

反应系统

回顾一下,事件源和变更数据捕获是可以用来构建响应式分布式系统(即微服务)的解决方案。微服务应该通过具有弹性和弹性来应对不断变化的环境(即云)。这些能力背后的魔力在于消息和事件驱动。要了解更多,我建议你阅读被动的宣言

图1。响应式系统的属性,根据响应式宣言

事件来源和变更数据捕获的共享目标

本文介绍的两个核心解决方案是事件来源和变更数据捕获。在我正式介绍这两种解决方案之前,可以知道它们的目标相似,分别是:

  1. 指定一个数据存储作为特定数据集的全局真实源

  2. 以一系列事件的形式提供过去和当前应用程序状态的表示,也称为日志或事务日志

  3. 提供一个可以根据需要重放事件的日志,以便重建或刷新状态

事件源使用它自己的日志作为真实源,而变更数据捕获依赖于底层数据库事务日志作为真实源。开云体育电动老虎机这种差异对软件的设计和实现有重大影响,本文稍后将介绍。

域事件与变更事件

在我们深入讨论之前,重要的是区分我们所关心的事件来源和更改数据捕获的事件类型:

  • 域事件——由应用程序生成的显式事件,是业务域的一部分。这些事件通常用过去时表示,例如OrderPlaced或ItemShipped。这些事件是事件源的主要关注点。

  • 更改事件——从数据库事务日志生成的事件,这些事件指示发生了什么状态转换。开云体育电动老虎机这些事件与变更数据捕获有关。

域事件和更改事件没有关联,除非更改事件碰巧包含域事件,这是本文后面介绍的发件箱模式的前提。

既然我们已经在事件来源和变更数据捕获方面建立了一些共性,那么我们可以进一步深入。

事件的采购

事件源是一种解决方案,允许软件将其状态维护为域事件的日志。因此,完整地获取日志表示应用程序的当前状态。有了这个日志,还可以轻松地审计历史,进行时间旅行,重现以前状态产生的错误。

事件源实现通常具有以下特征:

  1. 应用程序业务逻辑生成的域事件将为应用程序添加新的状态

  2. 应用程序的状态是通过一个通常是不可变的附加事件日志(日志)来更新的

  3. 在应用程序的生命周期内,日志被认为是真相的来源

  4. 日志可以重放,以便在任何时间点重新构建应用程序的状态

  5. 日志根据ID对域事件进行分组,以捕获对象的当前状态(DDD术语中的聚合)

图2。事件来源物化对象的表示

此外,事件源实现通常具有以下特征:

  1. 日志的快照机制,以加速重新创建应用程序的状态

  2. 根据需要从日志中删除事件的机制(通常出于遵从性原因)

  3. 用于事件调度的API,可用于分发应用程序的状态

  4. 缺乏强一致性系统通常存在的事务保证

  5. 向后兼容机制,以应对日志中不断变化的事件格式

  6. 备份和恢复日志的机制,日志是应用程序的真实来源

事件源模仿数据库的工作方式,但是是在应用程序级。开云体育电动老虎机根据图2,可以更新该图以表示图3所示的数据库,其设计大致相同。开云体育电动老虎机

图3。数据库事务物化表的表示开云体育电动老虎机

当我们深入研究事件来源和变更数据捕获之间的比较时,图2和图3之间的比较将变得更加相关。

变更数据捕获

变更数据捕获(CDC)是一种从数据库事务日志(或等效机制)捕获变更事件并将这些事件转发给下游消费者的解决方案。开云体育电动老虎机CDC最终允许应用程序状态外部化,并与外部数据存储同步。

变更数据捕获实现通常具有以下特征:

  1. 读取数据库事务日志的外部进程,其目标是将这些事务中的变更事件具体化开云体育电动老虎机

  2. 变更事件作为消息转发给下游消费者

如您所见,CDC是一个相对简单的概念,范围非常狭窄。它只是将数据库的事务日志作为事件流外部化给感兴趣的使用者。开云体育电动老虎机

图4。更改数据捕获实现选项

CDC还为您提供了使用事件的灵活性。图4:

  • 选项1是一个独立的CDC流程,用于捕获事务日志中的事件并将其转发到消息代理

  • 选项2是一个嵌入式CDC客户端,它直接向应用程序发送事件

  • 选项A是另一个连接器,它将CDC事件直接保存到数据存储区

  • 选项B通过消息代理将事件转发给消费应用程序

最后,CDC实现通常具有以下特征:

  1. 持久消息代理用于向所有使用者转发具有至少一次传递保证的事件

  2. 能够重放来自数据存储事务日志和/或消息代理的事件,只要这些事件是持久化的

CDC非常灵活,适用于多个用例。CDC的早期采用者选择了选项1/A,但选项1/B,以及选项2随着CDC的发展越来越受欢迎。

使用CDC实现发件箱模式

发件箱模式的主要目标是确保在单个事务中完成对应用程序状态(存储在表中)的更新和各自域事件的发布。这涉及到在数据库中创建一个Outbox表,以收集这些域事件作为事务的一部分。开云体育电动老虎机围绕域事件及其通过发件箱传播提供事务保证对于跨系统的数据一致性非常重要。

事务完成后,CDC连接器拾取域事件,并使用可靠的消息代理将域事件转发给感兴趣的消费者(参见图5)。然后,这些消费者可以使用域事件来实现自己的聚合(参见上面的每个事件来源)。

图5。用CDC实现的发件箱模式(2个选项)

发件箱还应该从应用程序中抽象出来,因为它只是外发事件数据的临时存储,不应该被读取或查询。事实上,驻留在发件箱中的域事件可以在插入后立即删除!

事件来源日志与发件箱

现在,我们可以更仔细地查看Event Sourcing日志和CDC与发件箱在设计上的重叠。通过比较日志与发件箱表的属性,可以清楚地看到它们的相似之处。聚合(同样来自DDD)是发件箱和事件源如何存储和使用数据的核心。

以下是存在于事件源日志和发件箱之间的常见属性:

  • 事件ID——事件本身的唯一标识符,可用于幂等消费者的重复数据删除

  • 聚合ID -用于划分相关事件的唯一标识符;这些事件组成了聚合的状态

  • 聚合类型——聚合的类型,只能用于将事件路由到感兴趣的使用者

  • 序列/时间戳——一种对事件进行排序以提供排序保证的方法

  • 消息有效负载——以下游使用者可读的格式包含要交换的事件数据

发件箱表和事件来源日志本质上具有相同的数据格式。主要的区别在于,Event Sourcing日志是一个永久的、不可变的域事件存储,而发件箱是高度短暂的,仅是在变更事件中捕获域事件并转发给下游消费者的着陆区。

命令查询职责隔离

命令查询责任隔离模式(简称CQRS)通常与事件源相关联。然而,使用CQRS并不需要事件源。例如,CQRS模式可以用发件箱模式代替。

那么到底什么是CQRS ?这是一种创建数据替代表示(称为投影)的模式,主要目的是在一些数据集上创建只读的可查询视图。对于不同客户感兴趣的同一组数据,可能会有多个预测。

CQRS的Command方面应用于应用程序处理动作(Commands),并最终生成可用于为投影创建状态的域事件。这就是为什么CQRS经常与事件源联系在一起的原因之一。

CQRS与事件源匹配良好的另一个原因是应用程序无法查询日志。在事件源系统中查询数据的唯一可行方法是通过投影。请记住,这些预测最终是一致的。这带来了灵活性,但也带来了复杂性,并且偏离了开发人员可能熟悉的强烈一致的视图的规范。

图6。用CQRS表示事件来源

图7。使用消息代理的CQRS事件源表示

正如您在图6和图7中看到的,这是基于事件源的CQRS模式的两种非常不同的解释,但最终结果是相同的,都是仅源于事件的数据的可查询投影。

如前所述,CQRS还可以与发件箱模式配对,如图8所示。这种设计的一个优点是在应用程序数据库中仍然有很强的一致性,但最终与CQRS投影保持一致。开云体育电动老虎机

图8。用CQRS表示发件箱模式

内部处理域事件

虽然本文主要关注在系统中分布数据,但在应用程序内部使用域事件也很重要。由于各种原因,在内部处理域事件是必要的,其中包括在与事件起源相同的微服务上下文中执行业务逻辑。这是构建事件驱动应用程序的常见实践。

无论是使用事件源还是CDC,在内部处理域事件都需要调度程序机制在内存中传递事件。Vert就是一个例子。x EventBus、Akka Actor System或Spring Application Events。在发件箱模式的情况下,只有在初始发件箱事务成功完成后才会分派事件。

属性比较

这篇文章已经向您抛出了很多内容,所以列出一个总结到目前为止所呈现内容的表格可能会对您有所帮助:

属性 事件的采购 疾病预防控制中心 CDC +发件箱 CQRS

目的

捕获包含域事件的日志中的状态。

从事务日志中导出变更事件。

通过CDC从发件箱导出域事件。

使用域事件来生成数据的投影。

事件类型

域的事件

更改事件

域事件嵌入在变更事件中

域的事件

真相之源

杂志

事务日志

事务日志

取决于实现

边界

应用程序

系统

系统(CDC)应用(发件箱)

应用或系统

一致性模型

-(仅写给《华尔街日报》)

强一致(表),最终一致(变更事件捕获)

强一致(发件箱),最终一致(变更事件捕获)

最终一致

Replayability

是的

是的

是的

取决于实现

事件源+ CQRS的优缺点

现在,我们已经更好地处理了事件源和CQRS,让我们研究一下当与CQRS配对时,事件源的一些优点和缺点。这些优点/缺点考虑了当前可用的实现,也记录了我和其他构建分布式系统的专业人员的经验。

CQRS事件源的优点

  1. 日志很容易用于审计目的

  2. 通常用于对日志进行大量写操作

  3. 对于大量数据(取决于数据存储)对Journal进行分片的可能性

CQRS事件源的缺点

  1. 一切最终都是一致的数据;强一致性数据的要求不适合事件源和CQRS

  2. 无法读取自己写到日志中的内容(从查询的角度看)

  3. 关于日志和事件源架构的长期维护关注

  4. 需要编写大量代码来补偿错误情况下的操作

  5. 没有真正的事务保证来解决双写入缺陷(接下来将讨论)

  6. 当事件格式发生变化时,需要考虑遗留数据的向后兼容性或迁移

  7. 需要考虑快照日志和与之相关的含义

  8. 拥有使用事件源和CQRS经验的开发人员的人才库几乎不存在

  9. 事件来源用例的缺乏限制了适用性

事件源和CQRS的双写入风险

事件源的一个问题是,如果应用程序出现错误,则有可能无法更新CQRS预测。这可能导致数据丢失,不幸的是,如果应用程序本身没有适当的补偿操作,可能很难恢复这些数据。这是由开发人员承担的额外代码和复杂性,而且很容易出错。例如,一种解决方法是跟踪与事件源日志相关的读偏移数,以便在错误时重新处理域事件并刷新CQRS投影,从而提供可重玩性。

出现这种错误可能性的根本原因是缺少向《华尔街日报》和CQRS预测撰写的事务。这就是所谓的“双重写入”,它极大地增加了出错的风险。这种双重写入缺陷如图9所示。

图9。事件源和CQRS缺乏事务完整性

即使添加消息代理(如图7所示)也不能解决双重写入问题。使用这种设计,您仍然要将消息写入消息代理,可能会出现错误。

双写缺陷只是在CQRS中使用事件源时遇到的一些挑战的一个例子。此外,将日志作为真实来源的长期维护和第2天影响会随着时间的推移增加应用程序的风险。事件源也是大多数工程师不熟悉的范例,很容易做出错误的假设或糟糕的设计选择,最终可能导致重新构建系统的某些部分。

考虑到事件源与CQRS搭配使用的利弊,建议在确定这种设计之前寻找替代方案。您的用例可能适合事件源,但CDC也可能适合。

开云体育官方注册网址Debezium用于CDC和发件箱

开云体育官方注册网址Debezium是一个由Red Hat支持的开源CDC项目,在过去几年中逐渐流行起来。最近,Debeziu开云体育官方注册网址m通过对Quarkus Java微服务运行时的扩展增加了对发件箱模式的完全支持。

开云体育官方注册网址Debezium、Quarkus和发件箱提供了一个全面的解决方案,避免了双写缺陷,与事件源解决方案相比,对于普通开发团队来说,它通常是一个更实用的解决方案。

图10。CQRS发件箱模式的错误处理

专业的CDC +发件箱与Debezium开云体育官方注册网址

  1. 真实源保存在应用程序数据库表和事务日志中开云体育电动老虎机

  2. 事务保证和可靠的消息传递极大地降低了数据丢失或损坏的可能性

  3. 适合原型微服务架构的灵活解决方案

  4. 简单的设计更容易长期维护

  5. 是否可以读取和查询自己写的内容

  6. 在应用程序数据库中实现强一致性的机会;开云体育电动老虎机系统其余部分的最终一致性

与Debezium CDC +发件箱的缺点开云体育官方注册网址

  1. 读取事务日志和通过消息代理可能会出现额外的延迟;为了最小化延迟,可能需要调优

  2. Quarkus虽然很棒,但它是目前唯一一个现成的发件箱API;如果需要,还可以滚动自己的实现

结论

构建分布式系统,即使是使用微服务,也是非常具有挑战性的。这就是为什么像事件来源这样新颖的解决方案具有吸引力。但是,使用Debezium的CDC和发件箱通常是事件源的更好开云体育官方注册网址选择,并且与CQRS模式兼容。虽然事件源在某些用例中仍然有价值,但我鼓励您先尝试Debezium和发件箱。开云体育官方注册网址

埃里克·墨菲

Eric是Red Hat的高级架构师,他领导应用程序开发和OpenShift的咨询项目。埃里克住在西雅图附近。


关于Debe开云体育官方注册网址zium

开云体育官方注册网址Debezium是一个开源的分布式平台,它将现有数据库转换为事件流,因此应用程序几乎可以立即看到并响应数据库中提交的每一个行级更改。开云体育电动老虎机开云体育官方注册网址Debezium是建立在卡夫卡并提供卡夫卡连接监控特定数据库管理系统的兼容连接器。开云体育电动老虎机开云体育官方注册网址Debezium在Kafka日志中记录了数据更改的历史,所以你的应用程序可以在任何时候停止和重新启动,并且可以很容易地使用它没有运行时错过的所有事件,确保所有事件都被正确和完整地处理。开云体育官方注册网址Debezium是开源Apache许可证,版本2.0

参与

我们希望您觉得Debezium有趣开云体育官方注册网址且有用,并愿意尝试一下。在Twitter上关注我们@开云体育官方注册网址debezium在Zulip上和我们聊天,或加入我们的邮件列表与社区对话。所有的代码都是开源的GitHub上,因此在本地构建代码并帮助我们改进现有连接器并添加更多连接器。如果您发现了问题或对我们如何改进Debezium有想法,请告诉我们开云体育官方注册网址记录问题

Baidu
map