3

我编写了一个应用程序 (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电话?如果是这样,为什么它在两台不同的机器上表现不同?

有任何想法吗?

4

2 回答 2

4

虽然你已经想通了,但还有另一种选择。当您使用say scala的默认ThreadPool ie 时,通常会发生这种情况scala.concurrent.ExecutionContext.Implicits.global。它使用newFixedSizeThreadPool与机器一样大小number of cores的机器。

另一种方法是在ExecutionContext上面的未来调用中使用你自己的。即在您上面的代码中,添加以下行:

implicit val exec = ExecutionContext.fromExecutor(Executors.newCachedThreadPool())

然后,您的所有future电话都将使用上述ExecutionContext.

于 2013-08-01T06:38:42.953 回答
4

好的,我想通了。哦,那是多么痛苦。问题是我的“一劳永逸”任务是一项长期运行的任务。但是部署机器上的线程池大小最终比我的开发机器小。结果,这个长时间运行的任务掉进了线程池并阻塞了它。因此,AsyncResult长时间运行的计算可能会抢占 的处理(它实际上阻塞了长时间运行的计算的结果,但底线是它被阻塞了)。

在阅读了 Heather Miller 对 Scala 2.10 中futures 的非常好的解释之后,我注意到了blocking可以用来临时扩展单个future 的线程池大小的构造。blocking我用一个调用包裹了我的阻塞代码,这就成功了。据推测,这分配了一个额外的临时工作人员来处理我的阻塞呼叫,因此,这些AsyncResult东西得到了及时处理。

(注意,封装submitJob调用是没有用的,因为它本身并没有阻塞。我要做的就是进入submitJob调用并找到实际阻塞所在的位置并封装它。)

于 2013-08-01T02:50:45.333 回答