繁体   English   中英

Scala中的Akka和第三方Java库的最佳实践

[英]Best practices with Akka in Scala and third-party Java libraries

我需要在我的Scala / Akka代码中使用memcached Java API 此API为您提供同步和异步方法。 异步的返回java.util.concurrent.Future 这里有一个关于在Scala中处理Java Futures的问题如何在Akka Future中包装java.util.concurrent.Future? 但在我的情况下,我有两个选择:

  1. 在将来使用同步API和包装阻止代码并标记阻塞:

     Future { blocking { cache.get(key) //synchronous blocking call } } 
  2. 使用异步Java API并在Java Future上每隔n ms轮询一次以检查未来是否已完成(如上面链接问题中的上述答案之一所述)。

哪一个更好? 我倾向于第一种选择,因为轮询可以极大地影响响应时间。 不应该blocking { }阻止阻止整个池?

我总是选择第一个选项。 但我这样做的方式略有不同。 我不使用blocking功能。 (实际上我还没有考虑过它。)相反,我正在为Future提供一个包装同步阻塞调用的自定义执行上下文。 所以看起来基本上是这样的:

val ecForBlockingMemcachedStuff = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(100)) // whatever number you think is appropriate
// i create a separate ec for each blocking client/resource/api i use

Future {
    cache.get(key) //synchronous blocking call
}(ecForBlockingMemcachedStuff) // or mark the execution context implicit. I like to mention it explicitly.

因此,所有阻塞调用都将使用专用的执行上下文(= Threadpool) 因此它与主要执行上下文分开,负责非阻塞内容。

这种方法也在Typesafe提供的Play / Akka在线培训视频中进行了解释。 第4课中有一个关于如何处理阻塞呼叫的视频。 它由Nilanjan Raychaudhuri解释(希望我拼写正确),他是Scala书籍的着名作者。

更新:在推特上与Nilanjan进行讨论 他解释了blocking方法和自定义ExecutionContext之间的区别。 blocking功能只是创建一个特殊的ExecutionContext 它提供了一个简单的方法来解决您需要多少线程的问题。 每当池中所有其他现有线程都忙时,它就会生成一个新线程。 所以它实际上是一个不受控制的 ExecutionContext。 它可能会创建大量线程并导致内存不足错误等问题 因此,具有自定义执行上下文的解决方案实际上更好,因为它使这个问题变得明显。 Nilanjan还补充说,你需要考虑电路中断,因为这个池会因请求而过载。

TLDR:是的,阻止呼叫很糟糕。 使用自定义/专用ExecutionContext来阻止调用。 还要考虑断路。

Akka文档提供了一些关于如何处理阻塞调用的建议:

在某些情况下,进行阻塞操作是不可避免的,即让线程在不确定的时间内进入休眠状态,等待外部事件发生。 示例是传统的RDBMS驱动程序或消息传递API,其根本原因通常是(网络)I / O在封面下发生。 面对这种情况时,您可能想要将阻塞调用包装在Future中并改为使用它,但这种策略太简单了:当应用程序运行增加时,您很可能会发现瓶颈或内存或线程耗尽加载。

“阻塞问题”的适当解决方案的非详尽清单包括以下建议:

  • 在一个actor(或由路由器管理的一组actor)中进行阻塞调用,确保配置一个专用于此目的或足够大小的线程池。

  • 在Future中进行阻塞调用,确保在任何时间点对此类调用的数量进行上限(提交此类无限数量的任务将耗尽您的内存或线程限制)。

  • 在Future中执行阻塞调用,提供一个线程池,其中包含适用于运行应用程序的硬件的线程数上限。

  • 专门用于管理一组阻塞资源(例如,驱动多个通道的NIO选择器)并在事件消息发生时调度事件。

第一种可能性特别适用于本质上是单线程的资源,例如数据库句柄,传统上一次只能执行一个未完成的查询并使用内部同步来确保这一点。 一种常见的模式是为N个actor创建路由器,每个actor包含一个数据库连接并处理发送到路由器的查询。 然后必须调整数量N以获得最大吞吐量,这将取决于在哪个硬件上部署哪个DBMS。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM