如何保持RDMS和Kafka同步?

时间:2017-02-03 14:26:15

标签: apache-kafka cqrs event-sourcing

我们想要在我们的应用程序中引入一个Kafka事件总线,它将包含EntityCreatedEntityModified等一些事件,以便我们系统的其他部分可以使用它。主要应用程序使用RDMS(即postgres)来存储实体及其关系。

现在的问题是,如果您成功保存到RDMS,您将确保仅在Kafka上发送EntityCreated个事件。如果您不确定是这种情况,最终会导致消费者不一致。

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

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

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

2 个答案:

答案 0 :(得分:1)

  

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

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

  

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

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

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

这样做的好处是您的消费者无需担心消息排序;消费者正在阅读有序的事件的文件,并指出之前和之后的文件。

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

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

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

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

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

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

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

  

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

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

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

在你目前的设计中......

  

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

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

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

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

答案 1 :(得分:0)

如果您的要求是将SQL和kafka视为单个节点,则没有完美的方法来执行此操作。因此问题应该是:“如果发生的话,我可以承受哪些不良后果(电源故障,硬件故障)?如果必须将其应用于我的应用程序,可以进行哪些更改(编程,体系结构)?”

对于您提到的那些点:

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

所有这些对象都将面临数据不一致的风险(如果将数据插入sql不能成功一次以上(例如它们具有非数据库生成的pk),则4会更好一些)。

从更改的角度来看,3最小,但是,它将降低sql吞吐量。 4最大是因为您的业务逻辑模型在进行编码时将面对两种数据库(通过数据编码器写入kafka,通过sql语句从sql读取),它比其他数据库更具耦合性。

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