Scala 在Akka I/O是如何工作的?

Scala 在Akka I/O是如何工作的?,scala,scalability,websocket,actor,akka,Scala,Scalability,Websocket,Actor,Akka,当您需要执行I/O(即数据库操作)时,actor模型(在Akka中)是如何工作的 我的理解是,阻塞操作将抛出异常(并且由于Akka使用的Netty的事件性质,基本上破坏了所有并发)。因此,我必须使用未来的或类似的东西-但是我不理解并发模型 一个参与者可以同时处理多条消息吗 如果参与者在future(即future.get())中进行阻塞调用,则该操作是否仅阻塞当前参与者的执行;或者在阻塞调用完成之前,它会阻止所有参与者执行吗 如果它阻止了所有的执行,那么使用将来的辅助并发性(即,将来调用阻塞调用

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

我的理解是,阻塞操作将抛出异常(并且由于Akka使用的Netty的事件性质,基本上破坏了所有并发)。因此,我必须使用
未来的
或类似的东西-但是我不理解并发模型

  • 一个参与者可以同时处理多条消息吗
  • 如果参与者在
    future
    (即
    future.get()
    )中进行阻塞调用,则该操作是否仅阻塞当前参与者的执行;或者在阻塞调用完成之前,它会阻止所有参与者执行吗
  • 如果它阻止了所有的执行,那么使用将来的辅助并发性(即,将来调用阻塞调用是否仍然等于创建一个参与者并执行阻塞调用)
  • 处理多阶段过程(即从数据库读取;调用阻塞Web服务;从数据库读取;向数据库写入)的最佳方法是什么,其中每个步骤都依赖于最后一个步骤
  • 基本情况如下:

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

    有更好的方法实现这一点吗?

    阻塞操作不会在Akka中引发异常。您可以阻止来自参与者的呼叫(您可能希望将其最小化,但这是另一种情况)

  • 否,无法创建1个参与者实例
  • 它不会阻止任何其他演员。您可以通过使用特定的调度程序来影响这一点。Futures使用默认的dispatcher(通常是全局事件驱动的),因此它在池中的线程上运行。您可以选择要为参与者使用的调度程序(每个参与者或所有参与者)。我想,如果你真的想制造一个问题,你也许可以将完全相同的(基于线程的)分派器传递给未来和参与者,但这会让你的角色产生一些意图。我想如果你有大量的期货无限期阻塞,并且executorservice被配置为固定数量的线程,你可能会毁掉executorservice。所以有很多“如果”。只有在未来尚未完成的情况下才能获得区块。它将阻止您调用它的参与者的“当前线程”(如果您从参与者调用它,顺便说一下,这不是必需的)
  • 你不一定要阻止。您可以使用回调而不是f.get。你甚至可以在不阻塞的情况下编写未来。查看维克多关于“阿克卡光明未来”的演讲,了解更多详情:
  • 我会在步骤之间使用异步通信(如果步骤本身就是有意义的进程),因此在每个步骤中都使用一个参与者,其中每个参与者向下一个发送一条单向消息,也可能是向其他参与者发送一条单向消息,这些参与者不会阻止可以监督进程的进程。通过这种方式,您可以创建多个参与者链,在其前面可以放置一个负载平衡参与者,这样,如果一个参与者在一个链中阻塞,那么相同类型的另一个参与者可能不在另一个链中。这也适用于您的“上下文”问题,将工作负载传递给本地参与者,并将它们链接到负载平衡参与者后面

  • 至于netty(我想你指的是远程演员,因为这是netty在Akka中唯一的用途),如果你担心时间安排或妨碍netty以某种方式完成工作,请尽快将你的工作转交给本地演员或未来(回拨)

    阻塞操作通常不会引发异常,但等待将来(例如使用
    !!
    !!!
    发送方法)会引发超时异常。这就是为什么您应该坚持使用fire,并尽可能多地忘记,使用有意义的超时值,并在可能的情况下选择回调

  • akka参与者无法显式处理一行中的多条消息,但您可以通过配置文件使用
    throughput
    值。然后,如果参与者的消息队列不为空,则参与者将处理多条消息(即,其接收方法将按顺序调用多次):

  • 在一个actor内阻塞操作不会“阻塞”所有actor,但如果在actor之间共享线程(推荐使用),则在操作恢复之前,将阻塞dispatcher的一个线程。因此,尽可能多地编写期货,并注意超时值)


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

    Raymond和paradigmatic所说的,但是,如果您想避免线程池耗尽,您应该将所有阻塞操作包装在
    scala.concurrent.blocking

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

    这个问题比paradigmatic描述的更糟糕,因为如果您有几个阻塞操作,那么最终可能会阻塞线程池中的所有线程,而没有空闲线程。如果您的所有线程都被阻塞,而这些阻塞在另一个参与者/未来被调度之前是不会发生的,那么最终可能会导致死锁

    下面是一个例子:

    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 } 导入scala.concurrent.blocking ... 未来{ val image=阻止{load_image_from_low_low_media()} val enhanced=image.enhanced() 阻塞{ 如果