Play Framework:文件上传 - 阻止还是非阻止?

时间:2015-03-01 16:05:39

标签: scala playframework

给出Play文档中的示例代码:

def upload = Action(parse.temporaryFile) { request =>
  request.body.moveTo(new File("/tmp/picture/uploaded"))
  Ok("File uploaded")
}
  1. 如何处理100个同时缓慢上传的请求(线程数)?
  2. 上传文件缓冲在内存中还是直接流式传输到磁盘?

1 个答案:

答案 0 :(得分:10)

  

如何处理100个同时缓慢上传的请求(线程数)?

这取决于。使用的实际线程数并不相关。默认情况下,Play使用的线程数等于可用的CPU核心数。但这并不意味着如果你有4个内核,那么你一次只限于4个并发进程。 Play中的HTTP请求在Akka提供的特殊内部ExecutionContext中异步处理。在ExecutionContext中运行的进程可以共享线程,只要它们是非阻塞的 - 这是由Akka抽象出来的。所有这些都可以以不同的方式配置。请参阅Understanding Play Thread Pools

使用客户端数据的Iteratee必须进行一些阻止,才能将文件块写入磁盘,但要以小(快)足够的块完成,这不应导致其他文件上传到变得封锁

我更担心的是服务器可以处理的磁盘I / O量。 100个慢上传可能没问题,但如果没有基准测试你就无法说出来。在某些时候,当客户端输入超过服务器可写入磁盘的速率时,您将遇到麻烦。这在分布式环境中也不起作用。我几乎总是选择完全绕过Play服务器并直接上传到Amazon S3。

  

上传文件缓存在内存中还是直接流式传输到磁盘?

所有临时文件都流式传输到磁盘。在引擎盖下,使用iteratee库异步读取从客户端发送到服务器的所有数据。对于分段上传,也没有什么不同。客户端数据由Iteratee使用,它将文件块流式传输到磁盘上的临时文件。因此,当使用parse.temporaryFile BodyParser时,request.body只是磁盘上临时文件的句柄,而不是存储在内存中的文件。


值得注意的是,虽然Play可以以非阻塞的方式处理这些请求,但一旦完成将移动文件将阻止。也就是说,request.body.moveTo(...)将阻止控制器功能,直到移动完成。这意味着如果100个上传中的几个几乎同时完成,Play的内部ExecutionContext处理请求很快就会过载。 {2.3}中也弃用了moveTo的基础API,因为它使用FileInputStreamFileOutputStreamTemporaryFile复制到永久位置。文档建议您使用Java 7 File API,因为它更有效。

这可能有点粗糙,但更像这样的事情应该这样做:

import java.io.File
import java.nio.file.Files

def upload = Action(parse.temporaryFile) { request =>
    Files.copy(request.body.file.toPath, new File("/tmp/picture/uploaded").toPath)
    Ok("File uploaded")
}