我如何在Akka工作?

时间:2011-06-30 00:27:00

标签: scala scalability websocket actor akka

当您需要执行I / O(即数据库操作)时,actor模型(在Akka中)如何工作?

据我所知,阻塞操作会引发异常(并且由于Akka使用的Netty的性质,基本上会破坏所有并发性)。因此,我将不得不使用Future或类似的东西 - 但我不理解并发模型。

  1. 1位演员可以同时处理多条消息吗?
  2. 如果一个actor在future(即future.get())中进行阻塞调用,那么阻止当前actor的执行;或者它会阻止所有演员的执行,直到阻止通话完成?
  3. 如果它阻止所有执行,那么使用未来辅助并发(即,将来不会调用阻塞调用仍然等于创建一个actor并执行阻塞调用)?
  4. 处理多阶段进程的最佳方法是什么(即从数据库中读取;调用阻塞的Web服务;从数据库中读取;写入数据库)每个步骤依赖于最后一步?
  5. 基本情况是:

    • 我正在使用Websocket服务器,它将维护数千个会话。
    • 每个会话都有一些状态(即身份验证详细信息等);
    • Javascript客户端将向服务器发送JSON-RPC消息,该消息将把它传递给相应的会话actor,后者将执行它并返回结果。
    • 执行RPC调用将涉及一些I / O和阻塞调用。
    • 会有大量并发请求(每个用户将通过WebSocket连接发出大量请求,并且会有很多用户)。

    有没有更好的方法来实现这一目标?

3 个答案:

答案 0 :(得分:27)

阻止操作不会在Akka中抛出异常。你可以阻止来自Actor的调用(你可能想要最小化,但这是另一个故事)。

  1. 不,1个演员实例不能。
  2. 它不会阻止任何其他演员。您可以使用特定的Dispatcher来影响这一点。 Futures使用默认调度程序(通常驱动的全局事件),因此它在池中的线​​程上运行。您可以选择要为演员使用的调度员(每个演员或所有人)。我想如果你真的想要创建一个问题,你可能能够将完全相同的(基于线程的)调度程序传递给期货和演员,但这需要你的一些意图。我想如果你有大量的期货无限期阻塞并且执行器服务已被配置为固定数量的线程,你可能会炸毁执行器服务。所以很多'如果'。只有当Future还没有完成时,f.get才会阻止。它将阻止您调用它的Actor的“当前线程”(如果您从Actor调用它,顺便说一下这不是必需的)
  3. 你不一定要阻止。你可以使用回调而不是f.get。你甚至可以不受阻碍地创作期货。有关详细信息,请查看Viktor关于“akka的充满希望的未来”的演讲:http://skillsmatter.com/podcast/scala/talk-by-viktor-klang
  4. 我会在步骤之间使用异步通信(如果这些步骤本身就是有意义的进程),那么每个步骤都使用一个actor,其中每个actor都向下一个发送单向消息,也可能单向消息发送给其他一些actor这不会阻止哪个可以监督这个过程。通过这种方式,您可以创建可以制作多个actor的链,在它之前可以放置一个负载平衡actor,这样如果一个actor在一个链中阻塞,则另一个链中的另一个相同类型可能不会。这也适用于你的“上下文”问题,将工作量传递给本地参与者,将它们链接到负载平衡的角色后面。
  5. 至于netty(我假设你的意思是远程演员,因为这是在Akka中唯一使用netty的东西),尽快将你的工作传递给本地演员或未来(带回调)你担心时机或阻止netty以某种方式完成它的工作。

答案 1 :(得分:10)

阻塞操作通常不会抛出异常,但等待未来(例如使用!!!!!发送方法)会抛出超时异常。这就是为什么你应该尽可能地使用“即发即忘”,使用有意义的超时值并尽可能选择回调。

  1. akka actor无法显式连续处理多条消息,但您可以通过配置文件播放throughput值。然后,如果其消息队列不为空,则actor将处理多个消息(即,其接收方法将被顺序调用几次):http://akka.io/docs/akka/1.1.3/scala/dispatchers.html#id5

  2. 阻止actor内部的操作不会“阻止”所有actor,但如果你在actor之间共享线程(推荐用法),调度程序的其中一个线程将被阻塞,直到操作恢复。因此,尽量尽量编写期货,并注意超时值。

  3. 3和4.我同意Raymond的回答。

答案 2 :(得分:1)

Raymond和范例所说的,但是,如果你想避免使线程池挨饿,你应该在scala.concurrent.blocking中包装任何阻塞操作。

当然最好避免阻塞操作,但有时你需要使用阻塞的库。如果你将代码包装在blocking中,它会让执行上下文知道你可能阻塞了这个线程,所以它可以根据需要分配另一个。

问题比范式描述更糟糕,因为如果你有几个阻塞操作,你最终可能会阻塞线程池中的所有线程并且没有自由线程。如果你的所有线程都被阻塞,那么你可能会遇到死锁,直到另一个演员/未来被安排好后才会发生。

以下是一个例子:

import scala.concurrent.blocking
...

Future {
  val image = blocking { load_image_from_potentially_slow_media() }
  val enhanced = image.enhance()
  blocking {
    if (oracle.queryBetter(image, enhanced)) {
      write_new_image(enhanced)
    }
  }
  enhanced
}

文档为here