我已经编写了一个应用程序(Scala)Play,当我在本地计算机上运行它时,它的工作方式与我预期的完全相同。我正在处理的方法(可能有其他人遇到此问题,我没有进行过广泛检查)是一个由POST请求触发的方法。它返回一个Async对象,因为该方法完成的评估返回Future。
但是当我部署它时,这种方法的表现完全不同。我一直在追逐这一切,但我已经到了无法解释发生了什么的地步。
该方法如下所示:
def add = Action(parse.json) { request =>
Async {
val req = request.body.asOpt[TaskRequest] map { r =>
val job = createJob(r)
submitJob(job, r)
job map { ij =>
allowOrigin(Created(ij).withHeaders("Location" -> routes.Jobs.read(ij._id.stringify).url));
}
}
req.get
}
}
总的来说,这很简单。 createJob
方法返回Future[Job]
。 submitJob
调用是一种“即发即忘”调用来触发一些背景内容(我不想等待)。然后,当job
创建时,我会处理Result
结果以生成job
个对象。
所有这些都像我在本地机器上所描述的那样工作。但是当我部署它时,真的很奇怪。 submitJob
调用会触发长时间运行的计算。正如我所说,我对等待它不感兴趣,这就是为什么我甚至不使用它返回任何地方的Future
。
但这是奇怪的部分...在我的开发机器上,我的POST请求(直接调用add)的响应在作业创建后立即返回。但是在部署机器上,我只在submitJob
任务完成后得到响应!换句话说,我将此req
作为Future
看似与submitJob
调用完全无关(它当然不依赖于其结果),但在部署中环境Async
阻止在submitJob
完成后才会返回(即使req
几乎立即完成)。
这到底是怎么回事?我在代码中放了println
个语句。它几乎可以立即通过req.get
电话。但总体(HTTP)响应肯定会等到submitJob
完成。 Async
以某种方式了解submitJob
电话吗?如果是这样,为什么它在两台不同的机器上表现不同?
有什么想法吗?
答案 0 :(得分:4)
好的,我明白了。哦,这是多么痛苦。问题是我的“一劳永逸”任务是一项长期任务。但是部署机器上的线程池大小最终小于我的开发机器。结果,这个长时间运行的任务在线程池中丢失并堵塞了它。因此,AsyncResult
的处理可能会被长时间运行的计算所取代(它实际上阻止了长时间运行的计算结果,但最重要的是它被阻止了。)
在阅读Heather Miller对Scala 2.10期货的一个非常好的解释之后,我注意到blocking
构造可用于临时扩展线程池的大小以用于单个未来。我用blocking
调用包裹我的阻止代码,这就是诀窍。据推测,这会分配一个额外的临时工来处理我的阻塞调用,因此AsyncResult
内容得到及时处理。
(注意,包裹submitJob
调用是没用的,因为它本身并没有阻止。我必须做的是进入submitJob
调用并找到实际阻塞的地方并包装。)
答案 1 :(得分:3)
虽然你已经想通了,但还有另一种选择。当您使用say scala的默认ThreadPool即scala.concurrent.ExecutionContext.Implicits.global
时,通常会发生这种情况。它使用newFixedSizeThreadPool
,其大小为number of cores
。
另一种方法是使用您自己的ExecutionContext
以及以上的未来电话。即在上面的代码中,添加以下行:
implicit val exec = ExecutionContext.fromExecutor(Executors.newCachedThreadPool())
您的所有future
来电都将使用上述ExecutionContext
。