如何保持RDMS和Kafka同步?

问题描述 投票:0回答:2

我们想要介绍一个Kafka事件总线,它将包含一些事件,如EntityCreatedEntityModified到我们的应用程序中,因此我们系统的其他部分可以从中消耗。主要应用程序使用RDMS(即postgres)来存储实体及其关系。

现在的问题是如何确保只有在成功保存到RDMS后才能在Ka​​fka上发送EntityCreated事件。如果您不确定是这种情况,最终会导致消费者不一致。

我看到了三个解决方案,其中没有一个是令人信服的:

  1. 不在乎:非常危险,插入RDMS时可能会出现问题。
  2. 保存实体时,还要保存应发送到自己的表中的消息。然后有一个单独的进程从该表中消耗并发布到Kafka并在从该表中删除成功之后。这是一个安静复杂的实现,也看起来像一个反模式。
  3. 插入到RDMS中,保持(SQL-)事务处于打开状态,直到您成功写入Kafka,然后才提交。问题是您可能会在一段时间内保持RDMS事务处于打开状态。不知道问题有多大。
  4. 做真正的CQRS,这意味着你根本不保存到RDMS,而是从Kafka队列中构建RDMS。这似乎是理想的方式,但很难改造服务。此外,由于延迟而存在不一致的问题。

我很难在互联网上找到好的解决方案。

也许这个问题是广泛的,随意指出我适合更好的地方。

apache-kafka cqrs event-sourcing
2个回答
1
投票

保存实体时,还要保存应发送到自己的表中的消息。然后有一个单独的进程从该表中消耗并发布到Kafka并在从该表中删除成功之后。这是一个安静复杂的实现,也看起来像一个反模式。

事实上,这是Udi Dahan在他的演讲中描述的解决方案:可靠的Messaging without Distributed Transactions。它实际上非常接近“最佳实践”;因此,值得探讨为什么你认为它是一种反模式。

做真正的CQRS,这意味着你根本不保存到RDMS,而是从Kafka队列中构建RDMS。

不要!这就是怪物藏身的地方! (见下文)。

如果您正在使用“真正的CQRS”,那么您的主要用例将是您的作者在您的记录簿中使事件持久,并且消费者将定期轮询更新。想想“Atom Feed”,附加条件是条目和条目顺序是不可变的;你可以分享事件和事件页面;缓存失效不是问题因为,因为状态不会改变,所以事件表示“永远”有效。

这也有利于您的消费者不必担心消息排序;消费者正在阅读有序事件的文档,其中包含指向先前和后续文档的指针。

此外,您还获得了版本化故事的解决方案:您可以发送一个表示形式,然后在消费者轮询您时,协商内容,而不是广播同一事件的N个不同表示形式。

现在,轮询确实存在延迟问题;您可以通过广播更新公告来减少延迟,并通知消费者新事件可用。

如果您想降低错误轮询的速度(唤醒消费者对他们不关心的事件),那么您可以开始在通知中添加更多信息,以便消费者可以判断是否提取更新。

请注意,“唤醒并且可能轮询”是由单个事件单独触发的过程。 “唤醒并轮询此消息”是同一想法的另一种变体。我们播放了EmailDeliveryScheduled的瘦版本;并且负责该服务的服务回叫以询问电子邮件/事件的增强版本以及构建电子邮件所需的详细信息。

这些是“唤醒和使用通知”的特殊化。如果您的用例中无法承受轮询所需的额外延迟,则可以在隔离事件的表示中使用该状态。

但是,当该信息已作为可共享,可缓存的文档公开时,尝试重现有序的事件序列......这是一个非常不寻常的用例。我不担心它是一个需要解决的普遍问题 - 我的猜测是这些情况很少见,而且不容易推广。

请注意,以上所有内容都与消息有关,而与Kafka无关。请注意,消息传递和事件源已记录为不同的use casesJay Kreps写道(2013)

我在这里使用术语“log”而不是“messaging system”或“pub sub”,因为它更具体地讲述了语义,并且更加详细地描述了在实际实现中为支持数据复制所需的内容。

您可以将日志视为一种具有持久性保证和强排序语义的消息传递系统

记录簿应该是事件消息顺序的唯一权限。任何关心订单的消费者都应该阅读记录簿中的有序文件,而不是阅读无序文件并重新构建订单。

在你目前的设计....

现在的问题是如何确保只有在成功保存到RDMS时才在Kafka上发送EntityCreated事件。

如果RDBMS是记录簿(“真相”的来源),那么Kafka日志(尚未)。

你可以从这里经过许多温和的步骤到达那里;粗略地说,您将事件添加到现有数据库中,您从现有数据库中读取以写入kafka的日志;您使用kafka的日志作为(时间延迟的)事实来源来构建现有RDBMS的副本,将读取用例迁移到副本,将写入用例迁移到kafka,然后停用旧数据库。

卡夫卡的日志可能是也可能不是您想要的记录簿。 Greg Young已经开发了Get Event Store已经有一段时间了,has enumerated some of the tradeoffs(2016年)。课程的马 - 我不希望用一个编写良好的代码库将日志从其中一个转换到另一个,但我根本不能说出可能发生的额外耦合。


0
投票

如果您的要求是将SQL和kafka看作单个节点,那么没有完美的方法可以做到这一点。所以问题应该是:“如果发生了什么坏事(电源故障,硬件故障)我能承受什么?如果它必须适用于我的应用程序,我可以采取什么样的变化(编程,架构)?”

对于你提到的那些点:

  1. 如果在从sql删除之前插入到kafka之后节点失败怎么办?
  2. 如果在提交sql事务之前插入到kafka之后节点失败怎么办?
  3. 如果在提交kafka偏移量之前插入到sql之后节点失败怎么办?

所有这些都将面临数据不一致的风险(如果插入到sql的数据不能成功多次,例如他们有非数据库生成的pk,则4会稍微好一点)。

从变化的角度来看,3是最小的,但是,它会降低sql吞吐量。 4是最大的,因为你的业务逻辑模型在编码时会面对两种数据库(用数据编码器写入kafka,用sql语句从sql读取),它比其他人有更多的耦合。

因此,选择取决于您的业务。没有通用的方法。

© www.soinside.com 2019 - 2024. All rights reserved.