出于某种原因,我无法解决这个问题。我有一个运行Play的应用程序,呼叫Elastic Search。作为我设计的一部分,我的服务使用包含scala future的Java API,如此blog post所示。我已经更新了该帖子中的代码,提示它将执行一些阻塞I / O的ExecutionContext:
import scala.concurent.{blocking, Future, Promise}
import org.elasticsearch.action.{ActionRequestBuilder, ActionListener, ActionResponse }
def execute[RB <: ActionRequestBuilder[_, T, _, _]](request: RB): Future[T] = {
blocking {
request.execute(this)
promise.future
}
}
构建要发送给ES的查询的实际服务将executionContext作为构造函数参数,然后用于调用弹性搜索。我这样做是为了让播放使用的全局执行上下文不会让它的线程被ES的阻塞调用所束缚。 This S.O. comment提到只有全局上下文才会阻塞,所以这让我不得不创建自己的。在同一个帖子/答案中有很多关于使用ForkJoin池的信息,但是我不确定如何使用those docs中的内容并将其与{{{{}}结合起来。 3}}创建一个响应阻塞提示的执行上下文。
我认为我遇到的一个问题是,我不确定如何首先响应阻止上下文?我正在阅读hints in the blocking documentation,它使用的示例是一个无限的线程缓存:
请注意,在这里我更喜欢使用无界&#34;缓存的线程池&#34;,因此它没有限制。在阻止I / O时,我们的想法是你必须拥有足够的线程来阻止它。但是如果无界限太多,取决于用例,你可以稍后对其进行微调,这个样本的想法就是让你滚动。
这是否意味着使用我的ForkJoin支持的线程池,我应该尝试在处理非阻塞I / O时使用缓存线程并创建一个新线程来阻止IO?或者是其他东西?几乎我在网上找到的关于使用单独线程池的所有资源都倾向于执行best practices,即:
如何调整各种线程池在很大程度上取决于您的个人应用程序,超出了本文的范围。
我知道这取决于你的应用程序,但在这种情况下,如果我只想创建某种类型的阻塞感知ExecutionContext并理解管理线程的合适策略。如果Context专门用于应用程序的单个部分,那么我应该只是制作一个固定的线程池大小而不是首先使用/忽略blocking
关键字吗?
我倾向于絮絮叨叨,所以我会试着打破我在答案中寻找的东西:
blocking
的关键部分。对不起,这里有一个很长的问题,我只是想让你了解一下我在看什么,并且我一直试图绕过这一天超过一天需要一些外面的帮助。
编辑:为了清楚说明,ElasticSearch Service的构造函数签名是:
//Note that these are not implicit parameters!
class ElasticSearchService(otherParams ..., val executionContext: ExecutionContext)
在我的应用程序中启动代码我有类似的东西:
object Global extends GlobalSettings {
val elasticSearchContext = //Custom Context goes here
...
val elasticSearchService = new ElasticSearchService(params, elasticSearchContext);
...
}
我也正在阅读an example of ForkJoins vs threadpools,但尚未看到有关阻止提示的任何内容,我怀疑我可能需要查看源代码,看看它们是否扩展了BlockContext
特征。 / p>
答案 0 :(得分:1)
所以我挖掘了文档和Play's best practices,因为我正在处理的情况是
在某些情况下,您可能希望将工作分派给其他线程池。这可能包括CPU繁重的工作或IO工作,例如数据库访问。要做到这一点,首先应该创建一个线程池,这可以在Scala中轻松完成:
并提供一些代码:
object Contexts {
implicit val myExecutionContext: ExecutionContext = Akka.system.dispatchers.lookup("my-context")
}
上下文来自Akka,所以我在那里搜索他们提供的默认和类型的上下文,最终导致我documentation on dispatchers。默认值为ForkJoinPool,其管理块的默认方法是调用managedBlock(blocker)
。这导致我阅读说明的文件:
根据给定的阻止程序阻止。如果当前线程是ForkJoinWorkerThread,则此方法可能会安排在必要时激活备用线程,以确保在当前线程被阻塞时具有足够的并行性。
所以看起来如果我有一个ForkJoinWorkerThread
那么我认为我想要的行为将会发生。再看一下ForkJoinPool的来源,我注意到默认的线程工厂是:
val defaultForkJoinWorkerThreadFactory: ForkJoinWorkerThreadFactory = juc.ForkJoinPool.defaultForkJoinWorkerThreadFactory
这对我来说意味着如果我在Akka中使用默认值,那么我将获得一个以我期望的方式处理阻塞的上下文。
因此,再次阅读Akka文档似乎指定我的上下文:
my-context {
type = Dispatcher
executor = "fork-join-executor"
fork-join-executor {
parallelism-min = 8
parallelism-factor = 3.0
parallelism-max = 64
task-peeking-mode = "FIFO"
}
throughput = 100
}
将是我想要的。
当我在源代码中搜索时,我做了一些寻找blocking
或调用managedBlock
的用法,并找到了覆盖ThreadPoolBuilder中的ForkJoin行为的示例
private[akka] class AkkaForkJoinWorkerThread(_pool: ForkJoinPool) extends ForkJoinWorkerThread(_pool) with BlockContext {
override def blockOn[T](thunk: ⇒ T)(implicit permission: CanAwait): T = {
val result = new AtomicReference[Option[T]](None)
ForkJoinPool.managedBlock(new ForkJoinPool.ManagedBlocker {
def block(): Boolean = {
result.set(Some(thunk))
true
}
def isReleasable = result.get.isDefined
})
result.get.get // Exception intended if None
}
}
哪个似乎就像我最初要求的那样,是一个如何制作实现BlockContext的例子。该文件还有代码显示如何创建ExecutorServiceFactory,这是我所相信的
是executor
部分配置的引用。所以我想如果我想要做什么我会怎么做
一个完全自定义的上下文将扩展某种类型的WorkerThread并编写我自己的使用自定义workerthread的ExecutorServiceFactory,然后在属性中指定完全限定的类名,如this post advises。
我可能会选择使用Akka的forkjoin :)