c# - 非常大的文件上传到 webDAV 会导致 tomcat OOM 错误
问题描述
我们正在维护一个用 c# 编写的旧应用程序,它将文件(二进制)发送到在 tomcat 8 下运行的 webDAV。我对 webDAV 的了解很少。基本上,c# 客户端正在执行以下操作:
// Create PUT request
HttpWebRequest request = job.Context.HttpUtil.CreateWebRequest(WebRequestMethods.Http.Put, job.Context.Server, job.Resource.ServerPath);
// ...do some stuff to get the stream to send
// Now stream the data to the request
sourceStream.Position = 0;
request.ContentLength = sourceStream.Length;
if (DeserializeTask.IsSerializedData(sourceStream))
{
request.ContentType = "application/octet-stream";
}
else
{
request.ContentType = "text/plain";
}
Stream stream = request.GetRequestStream();
byte[] buffer = new byte[0x1000];
int bytes;
while ((bytes = sourceStream.Read(buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, bytes);
}
stream.Flush();
stream.Close();
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
job.Context.HttpUtil.LogResponse(response);
}
finally
{
response.Close();
}
这工作正常。但是,我们有很多用户可以同时上传非常大的文件的用例,并且我们收到了从 tomcat 抛出的 OOM 错误。增加 jvm 堆不是一种选择。我怀疑 webDAV 正在等待请求输入流完成并在写入输出流(文件)之前将字节缓存在内存中,从而耗尽堆。当文件很大时,这将是一个问题。
我的问题是是否有办法告诉 webDAV(通过 webDAV 的 web.xml servlet 设置、某些请求属性等)不要缓存输入流中的字节,而是在每 1K 数据到达时写入输出缓冲区. 我研究了 webDAV 设置和 http 请求设置,并没有看到任何看起来可以实现这一点的东西。还是我们自己编写一个 servlet 来做到这一点?
当 OOM 发生时,来自 catalina.out:
554080 Exception in thread "http-bio-8080-exec-462" Exception in thread "http-bio-8080-exec-350" at orgapachenamingresourcesProxyDirContextProxyDirContext.java1458)
554081 Exception in thread "http-bio-8080-exec-387" Exception in thread "http-bio-8080-exec-468" Exception in thread "http-bio-8080-exec-375" Exception in thread "http-bio-8080-Acceptor-0" Exception in thread "http-bio-8080-exec-364" in thread "http-bio-8080-exec-255" Exception in thread "http-bio-8080-exec-341" at orgapacheservletsDefaultServletDefaultServlet.java762)
554082 at orgapacheservletsDefaultServletDefaultServlet.java409)
554083 at servlethttpHttpServletserviceHttpServlet.java620)
554084 at orgapacheservletsWebdavServletserviceWebdavServlet.java378)
554085 at servlethttpHttpServletserviceHttpServlet.java727)
554086 at orgapachecoreApplicationFilterChainApplicationFilterChain.java303)
554087 at orgapachecoreApplicationFilterChainApplicationFilterChain.java208)
554088 at orgapachetomcatserverWsFilterWsFilter.java52)
554089 at orgapachecoreApplicationFilterChainApplicationFilterChain.java241)
554090 at orgapachecoreApplicationFilterChainApplicationFilterChain.java208)
554091 at orgapachecoreStandardWrapperValveStandardWrapperValve.java220)
554092 at orgapachecoreStandardContextValveStandardContextValve.java122)
554093 at orgapacheauthenticatorAuthenticatorBaseAuthenticatorBase.java501)
554094 at orgapachecoreStandardHostValveStandardHostValve.java170)
554095 at orgapachevalvesErrorReportValveErrorReportValve.java98)
554096 at orgapachevalvesAccessLogValveAccessLogValve.java950)
554097 at orgapachecoreStandardEngineValveStandardEngineValve.java116)
554098 at orgapacheconnectorserviceCoyoteAdapter.java408)
554099 at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
554100 at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
554101 at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
554102 at concurrentThreadPoolExecutorThreadPoolExecutor.java1145)
554103 at concurrentThreadPoolExecutor$WorkerThreadPoolExecutor.java615)
554104 at ThreadrunThread.java745)
554105 OutOfMemoryError: Java heap space
554106 OutOfMemoryError: Java heap space
554107 OutOfMemoryError: Java heap space
554108 OutOfMemoryError: Java heap space
554109 OutOfMemoryError: Java heap space
554110 OutOfMemoryError: Java heap space
554111 OutOfMemoryError: Java heap space
554112 OutOfMemoryError: Java heap space
感谢您的任何建议。 - 拍
解决方案
以下是一些可能有帮助的解决方案:
- 将附件上传到专用文件服务器,并在您的 put 请求中提供指向该资源的链接。除非您在其他地方已经拥有更现代的文件服务器,否则价格昂贵。
- 如果至少一个上传成功,您可以创建一个更优雅的实现,当接收到异常时等待(1 到 10 左右)之间的随机(这很重要)几秒钟,然后重试。直到所有上传完成。高级,这可能需要随着时间的推移进行一些调整,并且可能难以正确
- 发送前压缩或压缩二进制文件。因此,超过特定阈值的 blob 应该被压缩,或者如果压缩后仍然太大,则告诉用户将文件分成更小的块。不是很可扩展,但根据文件大小可能就足够了。
- 一些 webdav 实现支持块上传。不太可能,因为这是旧代码,我认为 webdav 服务器也不先进。迁移到更新/高级的 webdav 实现可能太昂贵了。
希望您在这里找到有用的东西,否则请告诉我是否可以提供任何帮助。
推荐阅读
- java - 为什么这段代码会导致堆栈溢出?为什么只需将 ==1 更改为 <2,然后它就可以工作了?
- c# - 淘汰赛 JS 选择初始值在 foreach 循环内未正确显示
- django - 在 django 中,如何从 url 声明中传递 RequestContext?
- awk - 如何针对另一个文件执行一个文件中的单词搜索并在一行中显示第一个匹配的单词
- python-3.x - 使用 nginx + tornado 处理 5000 个 websocket 连接时打开的文件过多
- python - 对于每次迭代,只将数据添加到 pandas 中空数据框的几列
- python - 为什么 PIL 和 matplotlib 在这里创建完全不同的图像?
- c# - 反序列化具有嵌套列表的集合期间出现 JsonReaderException
- python - 从自定义文本字段中提取年份和月份
- matplotlib - 线性回归