0

众所周知,Dispatcher 线程负责执行参与者消息。使用吞吐量参数,我们可以定义否。在移动到另一个参与者之前由调度程序线程处理的消息

但我不确定调度程序线程将如何选择演员?

比如说,我创建了 10,000 个参与者,其中一次只有 1000 个参与者接收消息,其余 9000 个参与者处于空闲状态,调度程序线程数为 200。

调度程序线程将按照哪个顺序选择参与者的消息。它是否也会检查空闲演员邮箱的消息?

谁能解释调度程序线程选择参与者邮箱消息的流程。

4

1 回答 1

2

来自 Lightbend 讨论论坛的 X-Post:https ://discuss.lightbend.com/t/actor-message-allocation-to-dispatcher-thread/6314

在我开始之前,简短的回答是“别担心”。我理解这种好奇,但在微观层面上它是不确定的,从宏观层面来看,您需要关心的唯一事情是在Dispatcher 文档中,例如常规调度程序、固定调度程序、fork-join 和线程之间的区别池执行器和消息排序文档中的排序保证。

另外,免责声明,我并不声称自己是调度程序内部的专家:我只是一个最终用户。但是我有点拖延了,我想我会分享一些我的调优观察结果,并为了好玩而浏览一下 Akka 源代码。有关更详细的答案,您还应该查看源代码。您正在寻找的大多数答案都将在akka-actor/src/main/scala/akka/dispatch文件夹中。

有了这个免责声明,让我先回答你的第二个问题。

“[调度员]是否也会检查空闲演员邮箱中的消息?”

调度程序实际上并不检查任何东西:它完全是反应式的。(我们稍后会看到。)它当然不会浪费任何时间检查空邮箱。这就是为什么单个调度程序可以扩展到数百万个参与者的原因。

您更一般的问题“调度程序线程将如何选择演员?” 更难以简单的方式回答。调度员的种类很多。几乎我能给你的每一个答案都会有一个例外。(例如,前面提到的固定调度程序,其中线程专用于特定参与者,而 CallingThreadDispatcher 设计用于测试并在当前线程上运行所有调用)。但是让我谈谈典型情况下的典型调度员。

调度程序不选择参与者,调度程序(在典型情况下)只是 Java ExecutorServices 的接口。典型场景如下所示:

  • 您将消息添加到 Actor 的邮箱。(从调度员的角度来看,我们有一个颠倒的世界观:我们与邮箱交互,而不是演员。)
  • 如果邮箱还没有被调度(如果它有消息,它可能已经被调度),邮箱会转到它的调度程序并自行调度。
  • 调度程序转到底层的 ExecutorService(比方说 ForkJoinExecutor)并将任务排入队列以处理邮箱。
  • Java ForkJoinExecutor是一个复杂的调度程序,我并不声称自己是专家。但简短的版本是每个线程都有自己的队列,但是当它有一个空队列时,它能够从其他队列中“窃取”任务。Java 实现还能够动态调整它正在使用的线程数,直至达到并行限制。这就是为什么我说“在微观层面”它是不确定的。窃取动态线程的工作非常有效,但它不是确定性的。
  • 在某些时候,与包含该消息的邮箱相关的任务将由执行程序选择,并且 Runnable 将被调用。.
  • Runnable 将首先处理邮箱中的系统消息,然后处理常规消息。这里也有各种各样的例外,如优先邮箱、存储、吞吐量限制等,但通常邮箱将处理消息(使用参与者的行为),直到邮箱为空或达到吞吐量限制之一。请注意,任务与邮箱而非消息相关联。

以上内容过于简单化,忽略了一些边缘情况和性能优化,但那是 30,000 英尺的视图。

我希望这会有所帮助,因为我知道它充满了异常(邮箱和调度程序的设计是灵活的)和复杂的。但是 Akka 是高度优化的并且非常高效。如果有任何 Akka 开发人员想介入,请告诉我我的描述在哪里马虎,请随意。但最终结果是我开始的地方:这里有多个抽象层,因此您获得的唯一排序保证是记录的那些,但是即使每条消息完成的工作很小并且消息数量为巨大的。

于 2020-04-22T01:01:53.267 回答