我正在使用 Java 和 Cassandra 进行事件溯源从头开始构建一个项目。
我的应用程序基于微服务,在某些用例中信息将被异步处理。我想知道消息队列(例如 RabbitMQ、ActiveMQ Artemis、Kafka 等)在这种环境下将发挥什么作用来改进技术堆栈,如果我不使用它,我是否了解场景。
我首先将像 RabbitMQ 这样的消息基础设施与像 Kafka 这样的事件流/存储/处理分开。这是为两个(或更多)不同目的而制作的两种不同的东西。
关于事件源,你必须有一个必须存储事件的地方。该存储必须是仅附加的,并支持基于身份的非结构化数据的快速读取。这种持久性的一个例子是EventStore。
事件溯源与 CQRS 结合在一起,这意味着您必须将更改(事件)投影到另一个可以查询的商店。这是通过将事件投影到该存储来完成的,这是处理事件以更改域对象状态的地方。重要的是要理解,使用消息基础设施进行预测通常是一个坏主意。这是由于消息传递的性质和两阶段提交问题造成的。
如果您查看事件如何持久化,您可以看到它们作为一笔事务保存到存储中。如果您随后需要发布事件,这将是另一笔交易。由于您正在处理两个不同的基础设施,因此事情可能会被破坏。
这样的消息传递问题是,通常保证消息“至少传递一次”,并且通常不保证消息的顺序。此外,当您的消息使用者失败并且 NACK 消息时,消息将被重新传递,但通常会晚一点,再次破坏序列。
无论是谁,排序和重复问题都不适用于像 Kafka 这样的事件流服务器。此外,如果您使用追赶订阅,EventStore 将保证仅按顺序交付一次事件。
根据我的经验,消息用于发送命令并实现事件驱动架构,以反应方式连接独立服务。另一方面,事件存储用于持久化事件,只有到达那里的事件才会被投影到查询存储并发布到消息总线。
确保您清楚发送(命令)和发布(事件)之间的区别。 Udi Dahan 在他关于“巴士和经纪人”的文章中谈到了这个话题。 在大多数情况下,当您进行事件溯源时,您“不”希望从已发布的事件重建状态。如果您需要状态,请查询技术权威/记录簿以获取历史记录,并从历史记录中重建状态。
另一方面,消息队列中的事件驱动活动应该没问题。当单个事件(加上订阅者的状态)拥有您需要的一切时,那么离开总线就可以了。
在某些情况下,您可能会两者都做。例如,如果您要更新缓存视图,您可以订阅各种 BobChanged
事件来了解缓存数据何时过时;要重建陈旧的视图,您需要重新加载历史记录并将其转换为更新的视图。
在事件源应用程序的世界中,消息队列通常允许您在生产者和消费者之间实现发布-订阅模式风格的通信。此外,它们通常会帮助您提供传递保证:哪些消息已传递给哪些订阅者,哪些消息没有传递给哪些订阅者。