【问题标题】:Streaming through Tomcat通过 Tomcat 流式传输
【发布时间】:2025-12-29 08:10:12
【问题描述】:

我已经使用 Tomcat 构建了一个流媒体中继服务器。

简单的想法是:一个客户端执行 POST,另一个客户端执行 GET。 servlet 生成一个 Thread 并执行简单的字节混洗,直到 InputSteam(来自 POST)为空。完成后关闭/回答两个请求。一切都很好,而且效果很好,但是:

Tomcat 似乎重用了请求对象甚至 InputStream 对象!每 10 个 POST 左右,InputStream 无法读取,因为已经关闭。仔细查看日志,我意识到之前的请求使用(并因此关闭)完全相同的 InputStream 对象。事实证明,即使是 HttpServletRequest 对象也是完全相同的。

这里发生了什么?为什么 Tomcat 会重用显然没有正确重置的对象?我已经尝试过 7.0.29 和 6.0.16 版本,同样的事情。

【问题讨论】:

  • 这种事情通常发生在您在请求完成后保留对资源的引用时。您是否在任何地方存储对请求、响应或流的引用?
  • 我没有缓存任何请求或响应对象,但事实证明字节混洗线程释放 InputStream 对象有点太晚了(在请求已经返回之后),所以我认为这是问题。虽然我同意永远不应该缓存这些对象,但 Tomcat 响应如此糟糕的事实令人非常失望!

标签: java tomcat streaming


【解决方案1】:

由于这是一个资源管理问题(您的线程持有InputStream 太长),您应该能够解决这个问题:让启动线程的servlet(或将任务提交给Executor ...您正在为此使用任何Executor,对吗?)等待线程完成(或Future 完成)并释放其资源。如果您将代码发布到字节泵线程(以及 servlet 的相关部分),我可以向您展示如何改进它们。

【讨论】:

  • 解决方案很简单,难的是找出问题所在。我只是为每个输入/输出流使用了一个小包装器。现在我的后台线程只引用包装器而不是直接引用流,我可以简单地在从请求返回之前将包装器内的引用清空(无论如何,该后台线程现在已经停止洗牌并且即将清理 - 有时这也是一个有点太长了)。
【解决方案2】:

更新:

结果是我生成的 Thread(它显然有对 Input 和 OutputStreams 的引用)并没有总是及时释放这些引用。谢谢克里斯托弗的指点!

虽然我同意缓存这些对象是一种非常糟糕的做法,而且永远不应该这样做,但令人失望的是 Tomcat 对它的反应如此糟糕。我希望有这样成熟的应用服务器更好......

【讨论】:

  • 可以禁用这些对象的重用。结果是 Tomcat 会产生更多垃圾,并且由于额外的 GC 活动,您的性能会下降。
  • 有趣。我该如何禁用它?在我的情况下,我只有很少的连接可以打乱大量数据,所以无论如何都没有那么多对象可以重用......
  • 如果您禁用此类重用,它将对您处理的每个请求的性能产生负面影响......而不仅仅是那些您保留对流的引用的请求。
  • 在 (Tomcat 系统属性](tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html) 页面上查找“recycle”。我不记得流是有自己的外观还是只是请求和响应对象。这个可能根本无法保护您。