【问题标题】:Apache HttpClient timeout exception after server responds服务器响应后 Apache HttpClient 超时异常
【发布时间】:2011-12-06 14:04:16
【问题描述】:

我使用 Apache HttpClient 4.1.2 将压缩的二进制数据(序列化的 Java 对象)发布到服务器。

有时(20% 的时间)客户端在接收响应时会超时,即使在服务器正确响应之后并在其自己的日志中记录了“200”响应。

事件的顺序是:

  • 客户端将数据 POST 到服务器 (T0)。
  • 服务器记录数据接收(T2,即 T + 2 秒)。
  • 服务器处理数据。
  • 服务器记录成功完成处理 (T45)。
  • 服务器访问日志显示完成的响应以“200”状态发出 (T46)。
  • 客户端阻止读取响应标头。
  • 客户端超时(T1800,即 POST 请求后 30 分钟,这是我使用的标准超时)。

服务器返回的响应正文是一个很小的,例如五个字节或更少的纯文本字符串,例如“好的”。

这里会发生什么?如果服务器没有响应,我可以理解超时;但是服务器已经响应,并且在客户端超时之前很久就及时记录了正确的响应。客户端似乎正在“尝试”读取响应,但会阻塞并最终超时。

客户端在 Windows XP 机器上运行;服务器是 Ubuntu。两者都在运行 Java 6(现在是 1.6.29)。

我在每个请求上创建一个新的 DefaultHttpClient 对象,并在每个请求后关闭它(并适当地释放其他资源)。

客户端在请求成功完成后消费和处置响应实体主体,但我们还没有达到这一点 - 超时发生在httpclient.execute(方法)调用的上下文中。

堆栈跟踪:

java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(Unknown Source)
    at org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer(AbstractSessionInputBuffer.java:149)
    at org.apache.http.impl.io.SocketInputBuffer.fillBuffer(SocketInputBuffer.java:110)
    at org.apache.http.impl.io.AbstractSessionInputBuffer.readLine(AbstractSessionInputBuffer.java:264)
    at org.apache.http.impl.conn.LoggingSessionInputBuffer.readLine(LoggingSessionInputBuffer.java:115)
    at org.apache.http.impl.conn.DefaultResponseParser.parseHead(DefaultResponseParser.java:98)
    at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:252)
    at org.apache.http.impl.AbstractHttpClientConnection.receiveResponseHeader(AbstractHttpClientConnection.java:281)
    at org.apache.http.impl.conn.DefaultClientConnection.receiveResponseHeader(DefaultClientConnection.java:247)
    at org.apache.http.impl.conn.AbstractClientConnAdapter.receiveResponseHeader(AbstractClientConnAdapter.java:219)
    at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:298)
    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
    at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:645)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:464)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754)
    at foo.StreamerClient.sendRequest(StreamerClient.java:312)
    at foo.StreamerClient.compressAndPostBinaryDataToFooServer(StreamerClient.java:287)
    at foo.StreamerClient.compressAndPostObjectsToFooServer(StreamerClient.java:238)

这是来自成功请求的线路级别日志记录。与不成功的请求唯一不同的是,不成功的请求在记录二进制数据后停止,半小时后再次弹出并记录超时。

[12-06 14:07:22.359][scheduler-3] D DefaultClientConnection   Sending request: POST /foo/bar HTTP/1.1 
[12-06 14:07:22.359][scheduler-3] D wire                      >> "POST /foo/bar HTTP/1.1[\r][\n]" 
[12-06 14:07:22.359][scheduler-3] D wire                      >> "Accept-Encoding: gzip,deflate[\r][\n]" 
[12-06 14:07:22.359][scheduler-3] D wire                      >> "Content-Type: application/octet-stream[\r][\n]" 
[12-06 14:07:22.359][scheduler-3] D wire                      >> "Content-Length: 401[\r][\n]" 
[12-06 14:07:22.359][scheduler-3] D wire                      >> "Host: foo.com[\r][\n]" 
[12-06 14:07:22.359][scheduler-3] D wire                      >> "Connection: Keep-Alive[\r][\n]" 
[12-06 14:07:22.359][scheduler-3] D wire                      >> "User-Agent: Apache-HttpClient/4.1.2 (java 1.5)[\r][\n]" 
[12-06 14:07:22.359][scheduler-3] D wire                      >> "Authorization: Basic fobar[\r][\n]" 
[12-06 14:07:22.359][scheduler-3] D wire                      >> "[\r][\n]" 
[12-06 14:07:22.359][scheduler-3] D wire                      >> "[several lines of binary data]" 
[12-06 14:07:24.265][scheduler-3] D wire                      << "HTTP/1.1 200 OK[\r][\n]" 
[12-06 14:07:24.265][scheduler-3] D wire                      << "Date: Tue, 06 Dec 2011 03:07:23 GMT[\r][\n]" 
[12-06 14:07:24.265][scheduler-3] D wire                      << "Content-Type: text/plain;charset=ISO-8859-1[\r][\n]" 
[12-06 14:07:24.265][scheduler-3] D wire                      << "Content-Length: 2[\r][\n]"
[12-06 14:07:24.265][scheduler-3] D wire                      << "Connection: close[\r][\n]"
[12-06 14:07:24.265][scheduler-3] D wire                      << "[\r][\n]" 

谢谢。

【问题讨论】:

    标签: java apache timeout httpclient


    【解决方案1】:

    我注意到回复说

    Content-Length: 2
    

    但我在[\r][\n] 关闭空白行之后没有看到任何内容,这些内容不应被视为内容。我认为这是你的问题。服务器应该说Content-Length: 0 或者它没有适当地刷新其缓冲区或其他东西。

    此外,服务器在标头中说:

    Connection: close
    

    服务器关闭连接还是客户端仍然连接?如果服务器还没有关闭,那么它就会以某种方式被卡住。

    希望这会有所帮助。

    【讨论】:

    • 感谢您的回复。我所包含的响应是成功的。 (不成功的会在记录任何入站有线流量之前超时)。我也注意到了 Content-Length: 2 的业务,但是在这种情况下,请求和响应都成功了,所以它一定是做对了;如果不出意外,服务器和客户端就如何处理响应达成一致。 Connection: close 是,我很确定,只是 HTTP 响应的标准最后一行,可能类似于脚本语言中的“EOF”。
    • 否,“Connection: close”表示响应后,服务器应该关闭连接。是这样做的吗?如果您查看服务器(或者可能是 netstat -an),您还能看到连接吗?见这里:docs.oracle.com/javase/1.5.0/docs/guide/net/http-keepalive.html
    • 它来自客户端和服务器。客户端可以请求服务器在响应后关闭连接。如果服务器说“连接:关闭”,那么最好关闭连接。如果连接没有关闭,那么服务器端有问题。
    • 感谢您提供的链接,这很有趣。我不知道保持活动是默认行为。我当然不想让这些连接保持活跃——我想在每次连接尝试时从头开始。您关于检查服务器以获取连接仍然打开的证据的建议是一个很好的建议,我会跟进它。如果即使在 Tomcat 记录了“200”响应后连接仍然打开,那将是值得追求的。
    • 当然可以。提醒 +1 工作。如果它最终解决了您的问题(或有帮助),请务必接受。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-16
    • 2018-01-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多