【问题标题】:EventStore partial ordering of events and other featuresEventStore 事件的部分排序和其他功能
【发布时间】:2011-11-21 14:11:43
【问题描述】:

我正在尝试将EventStore 评估为服务器软件内部的可靠排队机制。

MSMQ 作为替代方案失败了,因为它不支持部分排序​​,消息“对话”中的有序消息。并且由于它的 4MB 消息大小限制(可以通过部分排序来克服)。 SQL Service Broker 确实支持部分排序​​,但是以编程方式设置和管理起来很麻烦。

由于 EventStore 的文档确实很少,有 EventStore 经验的人可以提供以下帮助吗?

  • EventStore 是否支持事件的事务处理 - 即, 如果处理失败,是否可以回滚出队?
  • 在各种线程、进程或机器中使用多个阅读器, EventStore 是否强制将每个事件(?)分派给一个 阅读器(一次,可能在交易期间)
  • 假设以上是可能的,不同“对话”上的事件是否可以 以任意顺序同时阅读,而同一对话中的消息被 单独阅读并按顺序阅读?
  • 我读到 EventStore 基本上是“至少一次”交付。是吗 可能,使用某些存储提供程序,以确保“恰好一次” 送货?
  • 如何处理“毒”事件?处理期间出错的事件。也许 错误本质上是暂时的,可以重试。也许它本质上是永久的,并且 需要行政干预。
  • 如有必要,是否可以手动操作 EventStore 存储?它可以 在其他读者继续阅读的同时完成?

(我读到存储引擎中的事务不是必需的,但我仍然使用事务的语言来表示在 EventStore 级别替换事务的任何内容。如果在从事务切换到任何内容时存在关键的功能后果,请评论他们。我不需要马上了解每个方面,只需要希望来争取更多的时间去尝试。)

【问题讨论】:

    标签: cqrs neventstore


    【解决方案1】:

    虽然 EventStore 可能用于构建完整的队列,但它的设计从未考虑到这一点。这意味着在构建库时有很多固执己见的决定与您的问题所提出的要求背道而驰。

    例如,消息传递系统并不真正支持精确一次传递的概念。上面提到的其他事情,比如有害消息,并不是真正的问题,因为 EventStore 并没有以这种方式连接到消息管道中。

    您尝试解决的问题似乎不是 EventStore 可以帮助您的问题。因此,我建议评估一个成熟的消息队列,例如 RabbitMQ。

    另外,您的邮件中有哪些内容会使邮件大于 4MB?如果您正在推送文件或大型二进制流,为什么不将它们推送到某种高度可用的“全局”存储(如 Amazon S3),然后在消息中添加指向这些内容的指针?

    【讨论】:

    • 谢谢!该应用程序用于专用于保证在服务协议之间传递消息的服务器,但队列在应用程序内部,但可以路由任意消息。 S3太多了。外部存储太复杂了。你是说 RabbitMQ 满足所有这些要求吗?谢谢!
    • Rabbit 对于你的情况来说绝对值得一看,但我不能保证它符合你提到的一切。
    【解决方案2】:

    一些想法,即使您似乎对第一个答案很满意:

    • 如果您跟踪消息的因果关系,就会发生部分排序。有一些方法可以做到这一点。最简单的做法是简单地保留分布式网络中给定消息已经看到的所有节点的列表,并在您推送消息时附加到该列表中。我说的是消息,但我实际上指的是那个对话中的消息。

      现在,这可能适用于简单系统,但当您开始获得更复杂的系统时,您可能希望 Saga 跟踪消息的确切含义。

      不过,您可能需要在单个收件人节点上进行部分排序 - 然后 sagas,在消息流方面处于中心辐射模式对您没有帮助。那么也许您实际上需要为您正在使用的传输添加一些逻辑。

      一种算法称为vector clocks,另一种类似的算法称为version vectors。这是Go 中矢量时钟的示例实现 - 如果你想花几个小时配对,我正在为 F# 开发一个小型矢量时钟库 - 因为算法实际上非常简单。如果你想真正阅读一些有意义的东西,我推荐这本书 - Elements of Distributed Systems。第2-3、5章不错。

      然后,您可以在分布式系统中对对话进行部分排序。您无法通过队列获得此信息的原因是您的队列和节点之间存在网络鸿沟,并且如果与队列位于同一节点上的节点或进程出现故障,则因为它有消息在传输过程中,此消息将被重新排队和重新排序。如果你 NACK 消息也是一样的。您可以通过在队列和消费客户端上使用 2PC 来解决此重新排序问题,或者您可以按消息的矢量时钟对消息进行排序,或者您可以根据发布/发送应用程序中给出的序列 ID 对它们进行排序,或者您可以根据某些数据对它们进行排序,因为从消费者的角度来看,这在语义上是有意义的。选择权在你。

    • 至于其他要求,比如毒消息,你应该看看服务总线给你什么。我个人使用 MassTransit,它可以很好地处理有毒信息。这些是消费消息的一些失败模式:

      • 序列化错误 - 你犯了一个编程错误。修复你的开发过程,因为这些不应该发生。如果它们仍然发生,只需将它们移至毒物队列 - 这些消息可能已损坏。
      • 您的代码中出现未处理的异常 - 您犯了一个编程错误。同样,您的开发过程需要检查。除非你扔它让你的服务总线运行时将消息移动到毒队列。
      • 您可能会遇到写入本地消费者数据库的问题 - 这是一个操作问题,不应在代码中发生。杀死你的进程,因为你现在实际上不能从代码中做任何事情。 Naigos 或其他一些进程监视器应该告诉你的操作人员有什么问题需要紧急修复——因为如果你不能写入你的读取模型,那么你的读取模型可能无法为它的请求提供服务。 Puppet 或其他一些进程监视器可能会在稍后重新启动您的进程,然后您可以执行相同的步骤,假设一切正常,但是这一次,在您连接到数据库之前不要开始消耗您的队列(例如,这就是 NHibernate 在启动时对其静态初始化所做的事情) - 并在该重试逻辑之上实现重试策略,例如断路器。
    • 大型事件 - 确保您的队列 API 块太长的字节数组。 ZeroMQ 具有多部分消息。 AMQP/RabbitMQ 没有,所以你必须自己对它们进行分块,当然,这会迫使你再次重新排序。或者,您可以像我们其他人一样,在某个地方提供二进制位块的句柄,以便从中读取它。

    【讨论】:

    • 这篇文章很棒。谢谢你。我现在正在阅读一些关于这些主题的初步研究论文。对于这个项目,我最终使用 NOLOCK、READPAST 和比较和交换技术定制了一个 SQL 实现,以实现我需要的基于事务的低争用部分订单交付。 (两个表,dbo.Sequences 和 dbo.Messages)。也许 Vector Clocks 可以在未来的项目中提供帮助,让我避免在我的实现中存在的废弃序列的次要垃圾收集问题。
    猜你喜欢
    • 1970-01-01
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    • 2019-05-16
    • 2017-09-19
    • 1970-01-01
    • 2011-07-11
    • 1970-01-01
    相关资源
    最近更新 更多