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

Sok*_*lov 7 scala playframework

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

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

Mic*_*jac 10

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

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

Iteratee消耗的客户数据必须做一些阻挡,以写入文件块磁盘,但在小(快速)足够大块做,这应该不会导致其他文件上传到被阻塞.

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

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

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


值得注意的是,尽管Play可以以非阻塞方式处理这些请求,但一旦完成移动文件将会阻止.也就是说,request.body.moveTo(...)将阻止控制器功能,直到移动完成.这意味着如果100个上传中的几个在大约相同的时间内完成,Play的内部ExecutionContext处理请求可能很快就会过载.moveTo在Play 2.3中也不推荐使用底层API ,因为它使用FileInputStreamFileOutputStream复制TemporaryFile到永久位置.文档建议您使用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")
}
Run Code Online (Sandbox Code Playgroud)