什么可能导致套接字连接异常:连接超时?

我们有一个 Webstart 客户端,它通过使用 通过 HTTPS 发送序列化对象来与服务器进行通信。java.net.HttpsURLConnection

在我的本地计算机和位于我们办公室的测试服务器上,一切都运行良好,但我遇到了一个非常非常奇怪的问题,这个问题只发生在我们的生产和过渡服务器上(而且偶尔发生)。据我所知,这些服务器与我们办公室中的服务器之间的主要区别在于它们位于其他地方,并且与它们的客户端 - 服务器通信要慢得多,但是在此之前,它在生产中运行了很长时间。

无论如何,这是正在发生的事情:

  • 客户端在设置了读取超时等选项和属性(如 on)后,会调用它来获取要写入的流。Content-TypeHttpURLConnectiongetOutputStream()
  • 在这一点上,据我所知,客户端挂起了一段时间。
  • 然后,客户端将引发以下异常:
java.net.ConnectException: Connection timed out: connect
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.PlainSocketImpl.doConnect(Unknown Source)
    at java.net.PlainSocketImpl.connectToAddress(Unknown Source)
    at java.net.PlainSocketImpl.connect(Unknown Source)
    at java.net.SocksSocketImpl.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.connect(Unknown Source)
    at com.sun.net.ssl.internal.ssl.BaseSSLSocketImpl.connect(Unknown Source)
    at sun.net.NetworkClient.doConnect(Unknown Source)
    at sun.net.www.http.HttpClient.openServer(Unknown Source)
    at sun.net.www.http.HttpClient.openServer(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.New(Unknown Source)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(Unknown Source)

请注意,这不是一个 ,如果超时在建立连接之前过期,则方法上的方法会说它会引发。此外,当这种情况发生时,我可以打电话,我得到的响应代码为200。SocketTimeoutExceptionconnect()HttpURLConnectionconn.getResponseCode()

  • 在服务器端,在 的构造函数中抛出 an,它试图读取序列化标头,但失败,因为客户端从未获取要写入的内容。EOFExceptionObjectInputStreamOutputStream

如果它有帮助,以下是在调用之前进行的调用(已编辑以仅显示正在进行的调用,而不是执行此操作的代码的整个结构):HttpsURLConnectiongetOutputStream()

HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setUseCaches(false);
conn.setReadTimeout(30000);
conn.setRequestProperty("Cookie", cookie);
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-java-serialized-object");
conn.getOutputStream();

问题是,我不知道这些是如何发生的,特别是考虑到它只是偶尔发生(我能说出明确的活动模式),即使这样,客户端和服务器之间(相对)高延迟时也是如此。

鉴于到目前为止我能够找到的关于,我想知道这是否不是我们的服务器运行的网络上的某个网络或防火墙问题...但这对我来说没有多大意义,因为请求显然是通过servlet的。此外,在同一网络上运行的其他应用程序尚未报告类似的问题。java.net.ConnectException: Connect timed out

有没有人知道这可能是什么原因,甚至我应该调查什么?


答案 1

我们在与您的情况类似的案例中遇到了这些情况。通常在高负载下,不易在测试中重现。还没有修复它,但这是我们经历的步骤。

如果是防火墙问题,我们会收到“连接被拒绝”或 SocketTimeout 异常。

1)您是否能够在服务器上的访问日志中跟踪这些请求 - 它们是否显示HTTP状态200或404或其他东西?在我们的例子中,服务器(在本例中为 IIS)日志显示客户端关闭了连接,而不是服务器。所以这是一个谜。

更新:如果客户端总是得到200,那么服务器实际上已经发回了一些响应,但我怀疑响应字节大小(如果这记录在访问日志中)将显示与该请求的正常响应大小不同的值

如果它显示相同的响应大小,则您有一个(可能不合理)条件,即服务器实际上正确响应,但客户端没有收到响应,因为连接在两者之间的某个地方终止。

2)网络管理团队查看了TCP / IP流量,以确定哪个端(或中间路由器)正在终止HTTP / TCP-IP会话。一旦我们理解了哪一端终止了连接,就要看看为什么。知识渊博的人可以进行窥探

3)服务器上是否配置/限制了最大数量的请求 - 这是否限制了您的连接?

4) 是否有任何中间负载均衡器可以丢弃请求?

更新:我们想要但未完成的另一件事是在客户端和服务器之间创建静态路由,以减少两者之间的跃点数,并确保没有与网络连接相关的连接断开。查看 http://en.wikipedia.org/wiki/Static_routing

5)另一个建议是设置ConnectTimeout,看看这些是否具有更高的值。更新:你可能想试试conn.getErrorStream()

如果连接失败,但服务器仍发送了有用的数据,则返回错误流。如果未连接连接,或者如果服务器在连接时没有错误,或者如果服务器有错误但没有发送错误数据,则此方法将返回 null。

6) 还可以尝试在服务器上间隔 5 秒获取一组线程转储,以查看是否有任何线程在服务器上显示这些传入请求。

更新:截至今天,我们学会了忍受这个问题,因为我们每天400,000个请求中的失败率为200-300个,即0.00075%


答案 2

在我们的服务器上使用它时,我们也会遇到零星的超时。我们可以通过两件事来修复它:

  1. 通过使用特定的 ContentLength(将错误率从 ~150 降低到 10)setFixedLengthStreamingMode
  2. 如果发生超时,请重试(错误率从 10 到 0。在最多重试一次后,一切都通过)

伪代码:

//set timeouts to 6s
try{
 //open connection here and write etc.
 //use a timeout of 6s (since retry is in place)
} 
catch (java.io.InterruptedIOException e) {
 //read- or connection time out try again                 
} 

为什么会发生这种情况的另一个理论可能是:

在 HttpURLConnection/HttpsURLConnection 的文档中,可以阅读以下内容:

每个 HttpURLConnection 实例都用于发出单个请求,但与 HTTP 服务器的底层网络连接可以由其他实例透明地共享。

因此,现在仅调用是可以的,但调用也会终止其他用户/透明共享连接的套接字,然后在达到超时期限后,该连接将运行到SocketTimeOut中。close()disconnect()


推荐