正如简介中提到的,发送集成事件时存在一致性方面的挑战:如果底层执行失败,则不得发送集成事件。在上面的示例中,如果价格服务中的价格更改提交失败,则不会发送任何价格更改事件。但是,如果执行成功,则应发送集成事件。
原则上,这些一致性要求可以通过使用分布式事务来保证,即通过基于两阶段提交协议的XA标准。然而,分布式事务的声誉值得怀疑,因为它们实施起来很复杂,并且常常导致性能不佳。在许多情况下,此选项无论如何都不可用,因为框架不允许 XA 事务。例如,在本文所基于的项目中,REST 被定义为一种用于实现集成事件的技术。但是,REST 或 HTTP POST 请求不支持分布式事务。但不能假定消息代理等其他技术支持 XA 事务 - 例如,RabbitMQ 也不支持它们。
确保一致性的另一种方法是 Vernon 在“实现域驱动设计”中描述的基于 柬埔寨 whatsapp 数据 事件存储的方法。其工作原理如下:
所有集成事件都在发生事件的服务中序列化,并与应用程序数据存储在同一数据库中。存储集成事件的一个或多个表形成所谓的事件存储。在示例中,这与价格数据位于同一数据库中。
集成事件保留在事件存储中的同一事务中,同时业务应用程序数据也保留在事件存储中。
集成事件仅在事务成功提交后才实际发送。
该解决方案确保仅当发送服务中的业务逻辑成功完成时才发送集成事件。由于集成事件安全地保存在事件存储中,因此可以很容易地多次尝试传递,并且如果需要,如果重复发生故障,则可以进入受控和记录的错误处理。与没有事件存储的更简单的解决方案相比,这是一个显着的优势,其中首先完成数据库事务,然后从正在运行的请求或 Java 线程的上下文数据发送集成事件。如果使用此解决方案发送失败(也称为尽力而为 1 阶段提交),则集成事件就会丢失。
与两阶段提交相反,如果发送集成事件失败,所描述的解决方案不会回滚业务逻辑。在许多情况下,这并不是一个缺点 - 例如,如果价格变化或订单在延迟后报告给计费服务,这通常是可以接受的,或者比仅仅因为计费服务而必须拒绝订单的情况更好目前不可用。
Spring Data JPA 和应用程序事件中的实现
我们现在希望基于 Spring 应用程序事件和 Data JPA 在 Java 中实现上述方法。我们假设使用经典的关系数据库,并且集成事件应通过 HTTP POST 请求发送。