寻找消息总线实现,提供完整的ACID之间的东西

问题描述 投票:5回答:3

有人知道消息总线实现,它提供对一致性保证的精细控制吗?完全ACID太慢,没有ACID太错误。

我们目前正在使用Rhino ESB包装MSMQ进行消息传递。在分布式事务中使用持久的事务性消息传递时,MSMQ可以在等待I / O完成时阻​​止提交相当长的时间。

我们的消息分为两大类:业务逻辑和非规范化。后者占消息总线流量的很大比例。

业务逻辑消息需要保证完整的ACID,并且MSMQ已经证明非常适合这一点。

非规范化消息:

  1. 必须耐用。
  2. 在原始交易完成之前,不得处理。
  3. 可以多次处理。
  4. 即使原始交易回滚,也可以处理,只要遵守2)。

(在某些特定情况下,可能会放宽持久性要求,但识别和处理这些情况作为规则的例外会增加复杂性。)

所有非规范化消息都在进程中处理,因此不需要IPC。

如果重新启动该进程,则可以假定所有事务已完成(已提交或已回滚),并且必须恢复尚未处理的所有非规范化消息。重放已经处理的非规范化消息是可以接受的。

据我所知,处理事务的消息传递系统倾向于在完全ACID之间提供选择,或者什么也不提供,ACID会带来性能损失。在某些情况下,我们看到对TransactionScope#Commit()的调用时间长达几百毫秒,具体取决于发送的消息数量。

使用非事务性消息队列会导致在其原始事务完成之前处理消息,从而导致一致性问题。

我们系统的另一部分具有相似的一致性要求但复杂度较低,已经使用了类似于事务日志的自定义实现,并推断出对于这个用例肯定是一种选择,但我宁愿不实现低延迟,如果我不需要,P,并发,持久,事务性消息传递系统:P

在任何人想知道的情况下,要求非规范化消息的持久性的原因在于检测解除和解决desyncs可能分别非常困难和极其昂贵。人们确实注意到某些东西有点错误并且页面刷新无法修复它,因此忽略desyncs不是一种选择。

msmq message-queue distributed-transactions denormalization
3个回答
1
投票

这不是您正在寻找的答案,但Jonathan Oliver撰写了大量有关如何避免在消息传递中使用分布式事务并保持事务完整性的文章:

http://blog.jonathanoliver.com/2011/04/how-i-avoid-two-phase-commit/ http://blog.jonathanoliver.com/2011/03/removing-2pc-two-phase-commit/ http://blog.jonathanoliver.com/2010/04/idempotency-patterns/

不确定这对你有帮助,但是,嘿。


0
投票

事实证明,MSMQ + SQL + DTC甚至不提供我们需要的一致性保证。我们以前遇到过一个问题,即在排队的分布式事务已经提交到数据库之前处理消息,导致过时的读取。这是使用ReadCommitted隔离来使用队列的副作用,因为:

  1. 开始交易A.
  2. 在A中更新数据库表
  3. A中的队列消息
  4. 请求提交A.
  5. 消息队列提交A.
  6. 开始交易B.
  7. 阅读B中的留言
  8. 读取B中的数据库表,使用ReadCommitted < - 获取前A数据。
  9. 数据库提交A.

我们的要求是B在A的提交中读取表块,这需要Serializable事务,这会带来性能损失。

看起来正常的事情确实是实现必要的约束并保证自己,即使它听起来像重新发明轮子。

有人对此有何评论?


0
投票

如果你想手动完成这个,这是一个可靠的方法。它满足(1)和(2),它甚至不需要你在(3)和(4)中允许的自由。

  1. 生产者(业务逻辑)启动事务A.
  2. 在一个或多个表中插入/更新任何内容。
  3. 将相应的消息插入PrivateMessageTable(域的一部分,如果愿意,则取消共享)。这是将要分发的内容。
  4. 提交事务A.生产者现在可以简单可靠地执行其写入,包括插入消息,或者将所有内容都回滚。
  5. 专用分发器作业从PrivateMessageTable查询一批未处理的消息。
  6. 分销商开始交易B.
  7. 将未处理的消息标记为已处理,如果修改的行数与预期不同(两个实例同时运行?),则回滚。
  8. 将消息的公共表示插入PublicMessageTable(以任何方式公开公开的表)。为公共表示分配新的严格顺序ID。因为只有一个进程正在执行这些插入,所以可以保证这一点。请注意,该表必须位于同一主机上以避免2PC。
  9. 提交事务B.分发者现在已经使用严格的sequantial ID将每条消息分配给公共表一次。
  10. 消费者(可能有几个)从PublicMessageTable查询下一批消息,其Id大于其自己的LastSeenId。
  11. 消费者开始交易C.
  12. Consumer将其自己的消息表示插入到自己的表ConsumerMessageTable中(从而推进LastSeenId)。 Insert-ignore可以帮助防止多个实例运行。请注意,此表可以位于完全不同的服务器中。
  13. 提交事务C.消费者现在已经消耗了每个消息一次,消息的公共可用顺序与消息相同,而不会跳过消息。
  14. 我们可以根据消费的消息做任何我们想做的事情。

当然,这需要非常仔细的实施。

它甚至适用于数据库集群,只要只有一个写节点,并且读取和写入都执行因果关系检查。很可能拥有其中一个就足够了,但是我必须更仔细地考虑这个含义。

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