akka 为什么最多一次传递是参与者系统的默认设置?

rn0zuynd  于 2022-11-05  发布在  其他
关注(0)|答案(2)|浏览(167)

我正在为一个事件源服务使用参与者系统,我想了解为什么默认值是至多一次传递,也就是说,从一个参与者到另一个参与者的消息可能至多到达一次,但可能根本就不会到达。我正在与Akka合作,但我知道这通常是参与者模型实现的默认值。
在我看来,最多一次交付很容易导致静默失败/数据损坏,而最少一次交付的问题可以通过版本控制和标记消息来轻松修复。
(最多一次有效)特别是在偶尔丢失消息不会使系统处于不一致状态的情况下
我不明白如何从“偶尔丢失一条消息没什么大不了的”逻辑跳到“丢失这条特定的消息也没什么大不了的”。如果我真的不在乎收到这条消息,为什么要发送它呢?
示例:我们正在构建一个系统来计算文档中的字数。我们有一个执行元(M),它接收文档,将其拆分为多行,然后将每行发送给子执行元(C)来计算行中的字数。M的状态是文档中有多少个单词;它通过从它的孩子C接收包含一行中的单词数量的消息来更新该状态,并将该数量加到总数中。
在最多一次传递中:如果子执行元由于网络错误而丢失消息(如果是实际参与者上的错误,父参与者可以使用监督来修复),父参与者不会知道。它可以通过保存每个子参与者的Map来跟踪状态,以及它是否完成,并在收到回复时更新Map。但如果我们将计算稍微复杂一些,并从子参与者获得可变数量的回复,这很快就失去了控制。另外,我们刚刚建立了一个系统来确保消息被接收,所以我们已经开始尝试脱离最多一次传递的世界,特别是如果我们依赖它来重新传递计算消息给C,而不是仅仅知道当前计数被破坏。
至少一次传送:我们可以给予M的消息一个序列ID,并在父M中保存一个日志(C -〉ID)。这让我们知道如果一个消息到达两次,我们应该丢弃它。对我来说,这似乎更简单,而且如果子节点的任务变得更复杂,通常也更可扩展。

8ljdwjyq

8ljdwjyq1#

为什么它是默认值的简短答案是:

  • 在最多一次的基础上实现最多一次并不困难(配对请求和回复的询问模式是其中的主要方式),但是没有很好的方法来从至少一次中得到最多一次(例如,不产生至少一次的成本)
  • “最多一次”意味着或多或少的一件事,而不考虑上下文,而“至少一次"意味着实际上无限多的事情(例如,在多长时间没有回复后,进程可以决定发送失败,以及应该重试多少次?)......没有合理的默认含义,而且很可能甚至没有给定应用程序/服务的合理默认。

请注意,至少一次可能导致至少与最多一次一样多的不一致性,尤其是在处理非幂等性时,并且添加幂等性的一般方法确实很重要;相反地,在更高级别(并且在需要的地方)设计幂等性通常能够是轻得多的重量。

pgx2nnw8

pgx2nnw82#

最多一次交付的最常见用例是对大量事件(例如,页面浏览)进行计数(或收集其他统计信息)。
对于这些应用程序来说,偶尔丢失消息确实不是什么大问题:如果你在一分钟内有999999个事件而不是100万个,没有人会注意到,你也可以在以后重新陈述你的统计数据,而不会对任何事情产生太大的影响。
多算会是一个更大的问题:当数据被重述时,数字会下降(通常不是仅增加1,由于丢失或超时的确认信息,很容易产生 * 大量 * 差异(见下文)。而且这并不像您想象的那样容易避免(你会在你建议“日志”中保存多少个id?你会保存多久?这需要多少额外的内存?)查找的速度会有多慢?如何处理组件重新启动?如何确保重复的事件总是发送到同一个组件示例?此外,您还必须在系统的每个组件中实现这种记录保留功能)
但更重要的是,强制执行至少一次合同会使系统复杂得多,因此可靠性也会低得多。
在这个系统中,一个组件不能只是将消息发送到管道中,然后就把它忘记了。它必须等待从它下游的每个组件接收ack。如果ack没有及时到达,它必须再次发送相同的消息,并且一直这样做,直到接收到ack为止。想象一下,由于某种原因,下游的一些组件速度很慢(可能是内存利用率高)。确认超时,上游开始一次又一次地重新发送同一事件。同时,新消息不断堆积,并且没有得到确认,所以他们也会被怨恨。它以指数级的速度快速增长。这 * 增加了 * 已经 * 有问题 * 的组件的负载 *(使它变得更慢),以及对 * 所有其他 *,迅速使本已糟糕的情况变得更糟,并最终使整个系统崩溃。

相关问题