在Play中,每个请求都会产生一个Akka演员吗?

时间:2015-04-22 11:32:50

标签: scala playframework playframework-2.0 akka blocking

我已经读过Play是建立在Akka上的,所以我想知道是否每次传入请求都会产生一个演员服务。

以此控制器操作为例:

def upload = Action(parse.multipartFormData) { implicit request =>
  request.body.file("picture").map { picture =>
    val client = new AmazonS3Client
    client.putObject("my-bucket", picture.filename, picture.ref.file)
  }.getOrElse {
    BadRequest("File missing")
  }
}

上传是同步发生的,而且我经常看到一些例子试图在Future中包装这样的代码块。我认为如果这个请求是由Akka演员提供的,那么就不需要这样做了。

如果我对或错,请告诉我,以及您对使用阻止服务的建议。

1 个答案:

答案 0 :(得分:8)

我不认为Play为每个请求产生一个新的actor,而是使用一个actor池来处理它们。如果可以为数百万个请求产生数百万个参与者,那么就可以通过利用演员的消息队列来处理请求,这样做的目的只是处理一个请求。在某些时候应该有一个上限。

然而,

Play是否真的这样做是无关紧要的。默认情况下,所有Action都是异步处理的。您的代码与使用Future的{​​{1}}中包含的代码之间的唯一区别在于它将使用便捷方法来处理Action.async。最后,两者都将是Future

等功能
  

我认为如果此请求由Akka演员提供,则不需要这样做。

这不是真的,部分是由于上述原因。 Play使用可配置的线程池(actor使用)来处理请求。默认情况下,它的大小为每个核心一个线程。演员分享线程以便工作。这意味着如果你有4个核心/ 4个线程和100个参与者(随机数),如果你有4个阻止上传,你将有4个被阻止的线程。无论演员的数量多少,你仍然会遇到线程饥饿,而其他96人都没用。更糟糕的是,这意味着您的服务器将无法再处理任何请求,直到其中一个上传完成且其中一个线程不再被阻止。

如果您将代码包装在Request => Future[Result] 中并且使用单独的Future来阻止,则可以缓解此问题。包装在ExecutionContext并非如此够了,因为代码仍在阻塞。你必须在某个地方阻止,但不要在Play用于处理请求的默认Future中执行此操作。相反,您可以专门配置一个用于处理上传。

application.conf

ExecutionContext

用法:

# Configures a pool with 10 threads per core, with a maximum of 40 total
upload-context {
  fork-join-executor {
    parallelism-factor = 10.0
    parallelism-max = 40
  }
}

您可以阅读有关线程池here的更多信息。