实践中分布式系统中事件的部分排序



我们使用Symfony Messenger和Amazon SQS作为消息队列。我们不能保证事件的处理顺序与它们的调度顺序相同。

我们有Payment:

class Payment {
public static function create(): void;
public function setAsCharged(): void;
public function setAsFailed(): void;
}

我们接收一组发生在Payment上的动作,并将它们发送到队列:

$actions = [
['initialization', '2023-01-01 00:00:00'],
['charge', '2023-01-01 00:00:10']
];
foreach ($actions as $action) {
$messenger->dispatch(new PaymentEvent($action)));
}

当前实现

事件按顺序分配到消息队列,但由于SQS的工作原理,charge事件可以在initialization事件之前处理。抛出异常是因为Payment不存在。消息然后通过重试机制重试。同时,initialization事件在不同的worker上处理。重试charge,Payment已经存在。一切都很好。Payment状态正常

不建议这样做。不应使用异常来控制应用程序。此外,我们无法区分真正失败的事件和由于Payment创建时间太长而失败的事件。

观察到的解决方案,但非常复杂

我研究了一些在分布式系统中实现部分排序的算法,如:Lamport timestamp, Paxos, Raft

如果我理解正确的话,工人之间必须以某种方式进行交流。我们应该使用Redis, MySQL或任何其他持久存储,可以被所有的工人访问?

我们正在使用PHP,我没有找到任何示例或库实现使用。看起来这是一个非常复杂的问题,不值得这么做。特别是如果我们可以使用重试机制。

我们不想使用FIFO队列,因为性能较低。

我们正在尝试的另一个解决方案

我们目前正尝试使用流程管理器来解决这个问题。有了它们,我们可以将事件保存到类似缓冲区的东西中,当预期的消息被接收和处理时,我们去处理缓冲区中的另一个事件。

它开始变得非常复杂,必须针对我们建模的每个领域进行定制。

  1. 你认为重试机制足够好吗?
  2. 是否有一般情况下可以使用的不那么复杂的解决方案?
  3. 上面提到的算法是否适合我们的技术栈?

两个想法:

  • 如果您只需要部分(相对于全部)排序,那么很可能您可以根据需要拥有尽可能多的FIFO队列(最多可能事件的分区数量,这样在不同分区中没有两个事件具有排序要求),并根据该事件的排序要求将事件直接到适当的队列。
  • 或者,您可以利用域的某些方面来表示尚未看到的事件。例如,给定支付的charge事件如果出现在initialization事件之前,可能会导致基本的"此支付尚未初始化但已被收取"的状态。

最新更新