如何确保包括持久化到数据库和调度事件在内的复合命令是原子的?
How to ensure composite commands including persisting to db and dispatching events are atomic?
早上好,在我们的项目中,我们有一个 REST 控制器,看起来像这样(它只是伪代码,而不是确切的情况;这里我给出一个简单的例子来展示整体概念):
@Transactional
public ResponesEntity<Void> compisiteCreate() {
customerService.createCustomer()
productService.createProduct()
orderService.createOrder()
}
每个服务(客户、产品和订单)的工厂方法是一个单独的用例。它在数据库中保留一条记录,如果成功保留,则会调度相应的事件。此外,每个方法都用 @Transactional
注释,因为每个方法都由专用端点调用。这样我们确保如果抛出异常,记录既不会保存在数据库中,也不会针对特定场景调度事件。
现在的要求是有一个 compositeCreate
端点(如上所示)应该是原子的,即如果 createOrder
失败,createCustomer
和 createProduct
都会回滚。问题是事件。调度的事件是同步的,不能回滚(即发即弃)。因此,如果第三种方法失败但前两种方法正常,则与第一种方法相关的事件已经调度,但记录不会存储在数据库中。
我在这里的第一个想法是将此逻辑包装在某种“事件调度程序会话”中。在该会话中,事件不是同步调度的,而是排队直到会话结束。然后在控制器逻辑结束时,当内部代码无异常通过时,我们关闭将调度所有排队事件的会话。
伪代码可能如下所示:
@Transactional
public ResponesEntity<Void> compisiteCreate() {
eventDispatcher.startSession()
customerService.createCustomer()
productService.createProduct()
orderService.createOrder()
eventDispatcher.flushSession()
}
事件调度程序会话将与线程相关联(例如使用 ThreadLocal)以确保每个请求只有一个会话。
如果会话未启动,则同步调度事件,没有任何变化。
预期结果:
要么保留所有内容并分派所有事件,要么不保留任何内容且不分派任何事件。
问题:
这个想法有意义吗?也许有人已经看到了一些缺陷?
或者,也许这个想法是完全错误的?还有人知道如何解决这个问题吗?
How to ensure composite commands including persisting to db and dispatching events are atomic?
“发件箱模式”:将事件作为事务的一部分写入数据库;担心单独发布事件。
参见 Reliable Messaging without Distributed Transactions,作者:Udi Dahan
Either everything is persisted and all events are dispatched or nothing is persisted and no events are dispatched.
因为我们有物理限制(例如:有限的光速),所以我们不能保证全有或全无。我们可以处理较弱的声明,例如“至多一次”或“至少一次”。
“至少一次”是实际有用的较弱声明,但它确实意味着订阅者必须能够识别重复消息 - 换句话说,您需要幂等消息处理。
另见 de Graaw 2010:Nobody Needs Reliable Messaging
早上好,在我们的项目中,我们有一个 REST 控制器,看起来像这样(它只是伪代码,而不是确切的情况;这里我给出一个简单的例子来展示整体概念):
@Transactional
public ResponesEntity<Void> compisiteCreate() {
customerService.createCustomer()
productService.createProduct()
orderService.createOrder()
}
每个服务(客户、产品和订单)的工厂方法是一个单独的用例。它在数据库中保留一条记录,如果成功保留,则会调度相应的事件。此外,每个方法都用 @Transactional
注释,因为每个方法都由专用端点调用。这样我们确保如果抛出异常,记录既不会保存在数据库中,也不会针对特定场景调度事件。
现在的要求是有一个 compositeCreate
端点(如上所示)应该是原子的,即如果 createOrder
失败,createCustomer
和 createProduct
都会回滚。问题是事件。调度的事件是同步的,不能回滚(即发即弃)。因此,如果第三种方法失败但前两种方法正常,则与第一种方法相关的事件已经调度,但记录不会存储在数据库中。
我在这里的第一个想法是将此逻辑包装在某种“事件调度程序会话”中。在该会话中,事件不是同步调度的,而是排队直到会话结束。然后在控制器逻辑结束时,当内部代码无异常通过时,我们关闭将调度所有排队事件的会话。 伪代码可能如下所示:
@Transactional
public ResponesEntity<Void> compisiteCreate() {
eventDispatcher.startSession()
customerService.createCustomer()
productService.createProduct()
orderService.createOrder()
eventDispatcher.flushSession()
}
事件调度程序会话将与线程相关联(例如使用 ThreadLocal)以确保每个请求只有一个会话。 如果会话未启动,则同步调度事件,没有任何变化。
预期结果: 要么保留所有内容并分派所有事件,要么不保留任何内容且不分派任何事件。
问题: 这个想法有意义吗?也许有人已经看到了一些缺陷? 或者,也许这个想法是完全错误的?还有人知道如何解决这个问题吗?
How to ensure composite commands including persisting to db and dispatching events are atomic?
“发件箱模式”:将事件作为事务的一部分写入数据库;担心单独发布事件。
参见 Reliable Messaging without Distributed Transactions,作者:Udi Dahan
Either everything is persisted and all events are dispatched or nothing is persisted and no events are dispatched.
因为我们有物理限制(例如:有限的光速),所以我们不能保证全有或全无。我们可以处理较弱的声明,例如“至多一次”或“至少一次”。
“至少一次”是实际有用的较弱声明,但它确实意味着订阅者必须能够识别重复消息 - 换句话说,您需要幂等消息处理。
另见 de Graaw 2010:Nobody Needs Reliable Messaging