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

Kha*_*tor 3 scala blocking akka playframework playframework-2.0

我已经读过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")
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Mic*_*jac 8

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

不过Play是否真的这样做是无关紧要的.默认情况下,所有Actions都是异步处理的.你的代码,并包裹在你的代码之间的唯一区别Future使用Action.async的是它会使用一个方便的方法来处理Future.最后,两者都将是功能Request => Future[Result].

我认为如果这个请求是由Akka演员提供的,那么就不需要这样做了.

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

如果你将代码包装起来Future 使用单独ExecutionContext的块来阻止,你可以缓解这种情况.包装Future是不够的,因为代码仍然是阻塞的.你将不得不在某个地方阻止,但是不要在ExecutionContext用于处理请求的Play默认值中执行此操作.相反,您可以专门配置一个用于处理上载.


application.conf

# 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
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

implicit val uploadContext: ExecutionContext = Akka.system.dispatchers.lookup("upload-context")

def upload = Action.async(parse.multipartFormData) { implicit request =>
  request.body.file("picture").map { picture =>
    val client = new AmazonS3Client
    Future(client.putObject("my-bucket", picture.filename, picture.ref.file))(uploadContext)
  }.getOrElse {
    Future.successful(BadRequest("File missing"))
  }
}
Run Code Online (Sandbox Code Playgroud)

你可以阅读更多关于线程池在这里.