Tomcat偶尔会返回没有HTTP标头的响应

时间:2018-11-27 09:36:18

标签: http tomcat nginx java-metro-framework chunked

我正在调查Tomcat( 7.0.90 7.0.92)偶尔返回没有HTTP标头的响应的问题。

根据Wireshark捕获的数据包,Tomcat收到请求后,仅返回响应正文。它既不返回状态行也不返回HTTP响应标头。

它使下游Nginx实例产生错误“上游从上游读取响应标头时没有发送有效的HTTP / 1.0标头”,向客户端返回502错误,并关闭了Nginx和Tomcat之间的相应http连接。

此行为可能是什么原因?有没有可能使Tomcat表现为这种方式?还是在某些情况下可以剥离HTTP标头的东西?还是Wireshark无法捕获包含HTTP标头的帧?任何缩小问题范围的建议也将受到赞赏。

这是Wireshark的“关注HTTP流”的屏幕截图,显示了有问题的响应:

enter image description here

编辑:

这是相关部分的“ TCP流”(仅响应)的屏幕截图。似乎来自最后一个的第二个响应中的块看起来不错:

enter image description here

EDIT2:

我将此问题转发到了Tomcat用户邮件列表,并从开发人员那里得到了一些建议,供进一步研究:

http://tomcat.10.x6.nabble.com/Tomcat-occasionally-returns-a-response-without-HTTP-headers-td5080623.html

但是我还没有找到合适的解决方案。我仍在寻找能解决此问题的见识。

3 个答案:

答案 0 :(得分:4)

您遇到的问题源于与上游的单个连接中的多个请求的管道传输,如EugèneAdell 昨天在此处的回答所解释。

这是否是nginx,tomcat,您的应用程序中的错误,还是上述任何组合的相互作用,可能是另一个论坛的讨论,但是现在,让我们考虑一下最佳解决方案:

  
    

您可以发布您的nginx配置吗?具体来说,如果您在nginx中使用keepalive和非默认值proxy_http_version? – cnst 1小时前

  
     

@cnst我正在使用proxy_http_version 1.1keepalive 100 – 1个小时前的Noohaki Kohei

根据earlier answer to an unrelated question here on SO,但如上所述共享配置参数,您可能想重新考虑在前端负载均衡器(例如nginx)和后端之间使用keepalive功能的原因。应用服务器(例如tomcat)。

根据keepalive explanation on ServerFault in the context of nginx,直到最近在nginx的开发年份中,nginx的upstream上下文中的keepalive功能才得到支持。为什么?这是因为在建立新连接要比等待现有连接可用时要快得多的情况下,很少有使用Keepalive的有效方案:

  • 当客户端和服务器之间的等待时间在50ms +左右时,使用keepalive可以重用TCP和SSL凭据,从而显着提高了速度,因为不需要额外的往返路程即可获得连接已准备就绪,可以处理HTTP请求。

    这就是为什么您永远不要禁用客户端和nginx之间的keepalive(在httpserverlocation上下文中通过http://nginx.org/r/keepalive_timeout控制)。

    < / li>
  • 但是,当前端代理服务器和后端应用程序服务器之间的延迟约为1毫秒(0.001秒)时,使用keepalive是追逐Heisenbug的秘诀,而不会带来任何好处,因为建立连接所需的额外1毫秒延迟也可能小于等待现有连接可用的100毫秒延迟。 (这是对连接处理的过分简化,但是它只是向您展示了前端负载均衡器和应用程序服务器之间的keepalive可能带来的巨大益处,只要它们都位于同一地区。)

    这就是为什么在upstream上下文中使用http://nginx.org/r/keepalive很少是一个好主意的原因,除非您确实需要它,并且已根据上述要点特别验证了它可以产生您想要的结果

    (而且,为了清楚起见,这些要点与所使用的实际软件无关,因此,即使您没有遇到nginx和tomcat组合使用时遇到的问题,我仍然建议即使您决定退出nginx和tomcat之一或两者,也不要在负载均衡器和应用程序服务器之间使用keepalive。)


我的建议?

  • 使用默认值http://nginx.org/r/proxy_http_versionhttp://nginx.org/r/keepalive无法重现该问题。

  • 如果您的后端在前端的5毫秒之内,那么最肯定的是,从头开始修改这些指令甚至都不会获得任何好处,因此,除非您追逐Heisenbugs,否则您不妨继续这些特定的设置是最明智的默认设置。

答案 1 :(得分:2)

我们看到您正在重新使用已建立的连接来发送POST请求,并且正如您所说的那样,响应没有 status-line headers

  

Tomcat收到请求后,仅返回响应正文。

不完全是。它以 5d 开头,可能是 chunk-size ,这意味着最新的“完整”响应(带有 status-line headers )包含一个“ Transfer-Encoding:chunked ”标题。无论出于何种原因,您的服务器仍然认为上一个响应在它开始向您的上一个请求发送新响应时尚未完成。

似乎已确认丢失了一个大块,因为屏幕截图未显示结束前一个请求的最后一个块(值= 0)。请注意,最后一个响应以最后一个块结尾(显示的最后一个字节为0)。

是什么原因造成的?从技术上讲,先前的回答未完全解决。这可能是Tomcat,您的Web服务库,您自己的代码上的错误。甚至,您可能在完全回答上一个请求之前就太早发送了请求。

如果您比较实际发送给客户端的块大小,会丢失一些字节吗?是否所有缓冲区都被冲洗了?还要注意行尾(仅限CRLF与LF)。

我正在考虑的最后一个原因,如果您的响应包含从请求中获取的某种用户输入,则您可能会面对HTTP Splitting

可能的解决方案。

值得尝试在您的库级别禁用分块编码,例如,使用Axis2检查HTTP Transport

重用连接时,请在阅读所有先前的响应之前检查客户端代码,以确保您没有发送请求(避免重叠)。

进一步阅读

RFC 2616 3.6.1分块传输编码

答案 2 :(得分:0)

事实证明,JAX-WS RI v2.1.3使用的“ sjsxp”库使Tomcat具有这种行为。我尝试了其他版本的JAX-WS RI(v2.1.7),该版本不再使用“ sjsxp”库,因此解决了该问题。

在Metro邮件列表上发布了一个非常类似的问题:http://metro.1045641.n5.nabble.com/JAX-WS-RI-2-1-5-returning-malformed-response-tp1063518.html