我需要在我的Scala/Akka代码中使用memcached Java API.此API为您提供同步和异步方法.异步的返回java.util.concurrent.Future.这里有一个关于在Scala中处理Java Futures的问题如何在Akka Future中包装java.util.concurrent.Future?.但在我的情况下,我有两个选择:
在将来使用同步API和包装阻止代码并标记阻塞:
Future { blocking { cache.get(key) //synchronous blocking call } }
使用异步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来阻止调用.还要考虑断路.