Streaming large files in a java servlet我正在构建需要扩展的Java服务器。 Servlet之一将提供存储在Amazon S3中的图像。 最近在负载下,我的VM内存不足,这是在我添加了用于提供图像的代码之后,因此,我很确定流较大的servlet响应会引起麻烦。 我的问题是:从数据库或其他云存储读取数据时,如何编写Java Servlet以便将大型(> 200k)响应流回浏览器,是否有最佳实践? 我考虑过将文件写入本地临时驱动器,然后生成另一个线程来处理流,以便可以重新使用tomcat servlet线程。 这似乎很沉重。 任何想法将不胜感激。 谢谢。 如果可能,您不应将要提供的文件的全部内容存储在内存中。取而代之的是,获取数据的InputStream,并将数据分段复制到Servlet OutputStream。例如:
我确实同意toby,您应该改为"将它们指向S3 url"。 至于OOM异常,您确定它与提供图像数据有关吗?假设您的JVM具有256MB的"额外"内存,可用于提供图像数据。在Google的帮助下," 256MB / 200KB" =1310。对于2GB的"额外"内存(目前这是一个非常合理的数量),可以支持10,000个并发客户端。即便如此,1300个并发客户端仍然是一个很大的数目。这是您经历过的负载类型吗?如果不是,则可能需要在其他地方查找OOM异常的原因。 编辑-关于:
几周前阅读S3文档时,我注意到您可以生成可以附加到S3 URL的过期密钥。因此,您不必公开S3上的文件。我对这项技术的理解是: 您为什么不只将它们指向S3网址?从S3中获取工件,然后通过您自己的服务器将其流式传输给我,这使使用S3的目的无法实现,因为S3的工作是卸载带宽并将图像处理服务交付给Amazon。 我已经看到了很多代码,例如john-vasilef的(当前接受的)答案,在紧紧的循环中从一个流中读取块并将它们写入另一流中。 我要提出的观点是反对不必要的代码重复,而赞成使用Apache的IOUtils。如果您已经在其他地方使用过它,或者如果您正在使用的另一个库或框架已经依赖于它,那么这是一条已知且经过良好测试的行。 在以下代码中,我正在将对象从Amazon S3流传输到Servlet中的客户端。
6行定义明确的模式,具有正确的流关闭效果,看起来非常可靠。 我非常同意toby和John Vasileff的看法,如果可以忍受相关问题,S3非常适合卸载大型媒体对象。 (自己的应用程序实例可以处理10-1000MB的FLV和MP4。)例如:不过,没有部分请求(字节范围标头)。必须"手动"处理,偶尔停机等。 如果那不是一个选择,John的代码看起来不错。我发现2k FILEBUFFERSIZE的字节缓冲区在微基准标记中是最有效的。另一个选项可能是共享的FileChannel。 (FileChannel是线程安全的。) 也就是说,我还要补充一点,猜测造成内存不足错误的原因是经典的优化错误。通过使用严格的指标,您将提高成功的机会。 当然还有其他工具,但是Java 5+附带的jmap和jhat都是"开箱即用"的
啊,我不认为你不能那样做。即使可以,听起来也很可疑。管理连接的tomcat线程需要控制。如果遇到线程不足,请增加./conf/server.xml中的可用线程数。同样,指标是检测到此问题的方法-不仅仅是猜测。 问题:您还在EC2上运行吗?您的tomcat的JVM启动参数是什么?
toby是正确的,如果可以的话,您应该直接指向S3。如果您不能这样做,那么这个问题可能会有点含糊,无法给出准确的答案: 如果使用8K缓冲区,则可能有1000个并发流进入?8Megs的堆空间,因此您肯定做错了...。 顺便说一句,我并不是凭空挑出8K的,这是套接字缓冲区的默认大小,发送更多的数据(例如1Meg),您将在tcp / ip堆栈上阻塞以容纳大量内存。 如果您可以对文件进行结构化,以使静态文件分离并位于各自的存储桶中,则可以通过使用Amazon S3 CDN CloudFront来实现当今最快的性能。 除了John建议的内容之外,您还应该重复刷新输出流。根据您的Web容器,它可能会缓存部分甚至全部输出,并一次刷新一次(例如,计算Content-Length标头)。那会消耗很多内存。 您必须检查两件事:
|