给出Play文档中的示例代码:
def upload = Action(parse.temporaryFile) { request =>
request.body.moveTo(new File("/tmp/picture/uploaded"))
Ok("File uploaded")
}
答案 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,因为它使用FileInputStream
和FileOutputStream
将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")
}