NServiceBus 和并发
NServiceBus and concurrency
这个 link 讨论了如何 NServiceBus 处理 sagas 的并发。但是,它没有显示说明何时可以发生并发的示例。在电子商务应用程序中,订单是针对每个用户的,因此不会出现多个用户更新同一个订单的情况。我没有看到订单系统和运输系统更新同一订单(订单传奇)的情况。它们发生在不同的时间。
除非开发人员编写代码让多个代理同时更新同一订单,否则是否会出现并发问题?
了解并发检查与哪种场景相关的关键:
If your endpoint runs with more than one worker thread, it is possible
that multiple messages will hit the same saga instance simultaneously.
假设您有多个线程处理同一条传入消息,这在设计为高性能的系统中并不罕见。为了便于讨论,假设我们有一个名为 "OrderAccepted" 的事件,它是订单从创建到履行的触发器。
如果您有多个线程处理该事件,并且相应的 saga 应该从头到尾执行此顺序,您只需要一个线程实际连接所述 saga 并开始处理。否则,您可能会遇到这样一种情况,即您可能会分配两次库存,两次付款,...
所以回答你的问题:当然允许不同的进程对不同的消息进行操作,一个进程可以实例化一个 saga 来创建订单,另一个进程可以实例化一个 saga 来在处理不同的订单时标记要发货的订单留言等
saga 并发检查试图解决的问题是竞争条件,在这种情况下,两个(或更多)进程都试图以相同的方式对消息进行操作,这可能会导致重复操作。
为了说明他们是如何做到这一点的,让我们给传奇一个修订(或版本)号。
- 一条消息被放在总线上,名为 "CreateOrder",orderID 为“1”
- 进程 A 和 B 都尝试处理消息
- 进程 A 实例化一个新的 saga,它的修订版本为 1
- 进程 B 几乎同时实例化了一个新的 saga,它也被赋予了 1 的修订
- 进程 A 的 saga 处理消息并且 saga 提交修订版 1
6/ 进程 B 的 saga 处理消息,尝试提交,注意到它的修订不匹配,因为修订 1 已经提交。然后它回滚它的动作。第一级重试(可能还有第二级重试)开始。
- 作为重试的一部分,进程 B 尝试再次处理消息,加载现有的 saga,idem-potency 检查开始,进程 B 注意到消息已经被处理并且什么都不做。
可能存在脏写的并发示例
一个简单的并发示例是当您有订单商品时。每个项目都由一条单独的消息添加。
它们共享相同的订单 ID。假设这些消息获取进程是并行的,现在可能会出现并发问题,因为两者都想扩展订单传奇,所以其中一个 必须 失败并重试,否则我们将进行脏写。
无限threads/workers
查看是否存在潜在并发问题的另一种方法是想象有无限线程能够处理您的完整消息积压,并考虑这是否会导致多个消息想要创建、更新或删除一个 saga实例,如果这可能会导致问题。
幂等性与多次传递
当同一消息被多次接收时,也会发生并发问题。这需要重复数据删除或其他形式的幂等处理。并非所有队列传输都提供 'only once delivery',因为其中很多都不是事务性的或配置为高可用性。
事件排序
即使事件本质上是按时间顺序排列的,因为您无法控制其他端点何时能够处理消息,所以始终期望事件到达或排序是一个很好的假设。
如果 saga 实例控制着特定的工作流程,您只能依靠它。
这个 link 讨论了如何 NServiceBus 处理 sagas 的并发。但是,它没有显示说明何时可以发生并发的示例。在电子商务应用程序中,订单是针对每个用户的,因此不会出现多个用户更新同一个订单的情况。我没有看到订单系统和运输系统更新同一订单(订单传奇)的情况。它们发生在不同的时间。
除非开发人员编写代码让多个代理同时更新同一订单,否则是否会出现并发问题?
了解并发检查与哪种场景相关的关键:
If your endpoint runs with more than one worker thread, it is possible that multiple messages will hit the same saga instance simultaneously.
假设您有多个线程处理同一条传入消息,这在设计为高性能的系统中并不罕见。为了便于讨论,假设我们有一个名为 "OrderAccepted" 的事件,它是订单从创建到履行的触发器。
如果您有多个线程处理该事件,并且相应的 saga 应该从头到尾执行此顺序,您只需要一个线程实际连接所述 saga 并开始处理。否则,您可能会遇到这样一种情况,即您可能会分配两次库存,两次付款,...
所以回答你的问题:当然允许不同的进程对不同的消息进行操作,一个进程可以实例化一个 saga 来创建订单,另一个进程可以实例化一个 saga 来在处理不同的订单时标记要发货的订单留言等
saga 并发检查试图解决的问题是竞争条件,在这种情况下,两个(或更多)进程都试图以相同的方式对消息进行操作,这可能会导致重复操作。
为了说明他们是如何做到这一点的,让我们给传奇一个修订(或版本)号。
- 一条消息被放在总线上,名为 "CreateOrder",orderID 为“1”
- 进程 A 和 B 都尝试处理消息
- 进程 A 实例化一个新的 saga,它的修订版本为 1
- 进程 B 几乎同时实例化了一个新的 saga,它也被赋予了 1 的修订
- 进程 A 的 saga 处理消息并且 saga 提交修订版 1 6/ 进程 B 的 saga 处理消息,尝试提交,注意到它的修订不匹配,因为修订 1 已经提交。然后它回滚它的动作。第一级重试(可能还有第二级重试)开始。
- 作为重试的一部分,进程 B 尝试再次处理消息,加载现有的 saga,idem-potency 检查开始,进程 B 注意到消息已经被处理并且什么都不做。
可能存在脏写的并发示例
一个简单的并发示例是当您有订单商品时。每个项目都由一条单独的消息添加。
它们共享相同的订单 ID。假设这些消息获取进程是并行的,现在可能会出现并发问题,因为两者都想扩展订单传奇,所以其中一个 必须 失败并重试,否则我们将进行脏写。
无限threads/workers
查看是否存在潜在并发问题的另一种方法是想象有无限线程能够处理您的完整消息积压,并考虑这是否会导致多个消息想要创建、更新或删除一个 saga实例,如果这可能会导致问题。
幂等性与多次传递
当同一消息被多次接收时,也会发生并发问题。这需要重复数据删除或其他形式的幂等处理。并非所有队列传输都提供 'only once delivery',因为其中很多都不是事务性的或配置为高可用性。
事件排序
即使事件本质上是按时间顺序排列的,因为您无法控制其他端点何时能够处理消息,所以始终期望事件到达或排序是一个很好的假设。
如果 saga 实例控制着特定的工作流程,您只能依靠它。