【问题标题】:Where and how to use actors in practice在实践中在哪里以及如何使用演员
【发布时间】:2017-11-21 02:49:08
【问题描述】:

actors 如何用于复杂的后端服务,即接收初始请求,进行一些处理,然后将一些请求/待办事项发送到其他服务,等待来自某些服务的响应他们,并根据响应决定如何继续,等等,直到我们计算出最终结果?

尝试使用参与者来实现此类服务提出了一个问题 - 该工作流程的哪些部分应该由参与者实现,哪些部分不应该?

Actor 实例在他们试图完成的任务完成之前不会释放他们正在使用的线程(除非我们将它委托给 Future),所以在我看来,它就像将整个工作流编写为一个越来越小的层次结构具有 N 层子节点的演员,一方面是基于演员概念的理想设计,但另一方面它将保持 N-1 个线程(每个初始请求) - 不断锁定,除了等待一个线程之外什么都不做层次结构中的底层参与者来完成。具体来说,层次结构中最顶层的参与者大部分时间都是空闲的。

这种分层设计也符合error kernel 模式。

但这听起来对并发非常不利。

即使你试图保持这个actors的层次结构,但是将每个actor的调用包装在一个Future中(通过询问子actors,而不是告诉子actors),所以锁定会更短(尽管它仍然存在) - 仍然使用如此多的 Futures 使得不可能与有状态的参与者合作,或者以自己不同步的方式修改系统状态的参与者(即不简单地修改数据库,至少对于简单的请求是自动完成的)通过数据库事务 - 而是修改应用程序中的一些全局变量)。

那么演员应该只用在工作流的较低级别吗?

或者应该以更连续的方式重写主要是分层的(并且大多数是)的工作流程,因此它不需要这么多级别的子演员(但这会非常困难和不自然,并且似乎给实现框架对设计影响太大)。

这意味着您认识到参与者非常有限,并且容错应该主要由传统的异常处理来处理,并且无论如何,拥有容错应用程序的愿望不应该对应用程序的设计。 既然如此,何必找演员呢?使用一个需要阅读每个参与者的实现以理解复杂的意大利面条状消息结的工作流程的框架,比使用基于层次结构的框架要容易得多查看方法签名,而不必查看它们的实现。

如果应用程序的一小部分由参与者实现,参与者的许多好处(例如易于横向扩展)似乎不太相关或不实用。

【问题讨论】:

    标签: multithreading error-handling akka actor error-kernel


    【解决方案1】:

    我认为您对演员“锁定”的理解是不正确的。

    你写

    但另一方面,它最多可以保持 N-1 个线程(每个初始 request) - 一直被锁定,什么都不做

    不清楚您在此处描述的是哪种情况。正在使用的线程数不是由参与者的数量或参与者层次结构中的层数决定的,而是由您运行参与者的调度程序决定的,请参阅https://doc.akka.io/docs/akka/2.5/dispatchers.html?language=scala

    actor 通常不会产生新线程,也不会在空闲时阻塞任何东西。它将处理(可配置的)数量的消息,然后将控制权交还给调度程序。因此,就多线程而言,Akka 与您所怀疑的相反,非常高效。

    如果你试图保持这种演员的层次结构,但将调用包装到 Future 中的每个演员(通过询问子演员,而不是 告诉),所以锁定会更短

    这里一定有什么误解。向另一个参与者发送消息的参与者不会阻塞线程。目前尚不清楚您指的是什么“锁定”。此外,ask 使锁定更短的假设显然是有缺陷的,因为没有锁定。

    也许您担心的是使用阻塞来自 actor 的 IO?那确实会阻塞执行线程。这里的“技巧”是将阻塞调用放在单独的调度程序上,请参阅https://doc.akka.io/docs/akka/snapshot/dispatchers.html?language=scala

    【讨论】:

    • 初始消息被发送到最顶层的actor A。为了处理它,线程A被分配给actor A。在处理过程中,actor A询问它的一个子actor B。处理这个问题,线程 B 被分配给参与者 B。参与者 A 需要参与者 B 的回答来决定如何继续处理初始消息。一直以来,线程 A 和 B 都在使用中。大多数时候,线程 A 是空闲的。您可以对演员 B 的子孙等重复此过程。我的理解有什么问题吗?
    • 是的。所有参与者 A 都知道,在向 B 发送消息后,它没有什么可做的了,所以它会屈服并释放线程 A 来做其他事情。演员 A 现在完成了。如果 B 应该在自己完成后向 A 发送消息,那么 A 将在另一个线程上再次运行(可能是 A、B 或完全是某个其他线程)。请注意,这与参与者层次结构无关,如果 B 是 A 的子级,则对此考虑没有区别。子actors并不意味着子线程。
    • 我认为您实际上证实了我的怀疑,即演员(或 Akka 实现)存在一些严重问题。不,演员 A 现在还没有完成。我明确地说“演员 A 需要演员 B 的回答才能决定如何继续处理初始消息。”一个演员可以在一个消息处理期间发送多个消息,在我的情况下,这是很常见的,额外的消息首先需要来自演员 B 的响应,以便知道如何继续。这就是为什么我让线程 A 等待未来 B 完成,然后再继续。
    • 您似乎建议我应该将演员 A 中的原始按摩处理中断为 2 个步骤,第二个步骤将开始处理来自演员 B 的响应消息(可以在演员 A 或其他人中完成演员)。我在我的问题中提到了这个选项(将主要是分层的工作流重写为更串行的工作流),这是有问题的。它将原始工作流程分解为许多单独的部分,这意味着推广意大利面条式代码。现在我得到了不可读的代码。还有一个更大的问题 - 它完全破坏了原始工作流程的原子性。
    • P.S.我很清楚,层次结构与这个问题无关。我只是从我想要实现的工作流程开始,然后这个工作流程规定了某个关注点的层次结构,然后在我设计层次结构时应该指导我。但可能导致线程过度使用的是工作流的概念层次结构及其时间线(而不是参与者的层次结构)。
    猜你喜欢
    • 2020-07-18
    • 2012-06-28
    • 2020-08-30
    • 2021-09-01
    • 2020-03-19
    • 2016-09-24
    • 2013-01-29
    • 1970-01-01
    • 2023-04-02
    相关资源
    最近更新 更多