代码之家  ›  专栏  ›  技术社区  ›  ColinD

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

  •  10
  • ColinD  · 技术社区  · 14 年前

    我们有一个Webstart客户机,它通过使用 java.net.HttpsURLConnection .

    在我的本地机器和位于我们办公室的测试服务器上,一切都非常正常,但我遇到了一个非常非常奇怪的问题,它只发生在我们的生产和登台服务器上(偶尔也会发生)。据我所知,这些服务器与我们办公室的服务器之间的主要区别在于,它们位于其他地方,与它们的客户机-服务器通信速度要慢得多,但在此之前,它在生产中也运行了很长一段时间。

    不管怎么说,发生了这样的事:

    • 客户端,在设置了诸如读取超时之类的选项和诸如 Content-Type HttpURLConnection ,电话 getOutputStream() 在它上面得到要写入的流。
    • 此时,据我所知,客户机会挂起一段时间。
    • 然后,客户端抛出以下异常:
    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)
    

    注意,这不是 SocketTimeoutException ,其中 connect() 方法打开 可设定代理 表示如果超时在建立连接之前过期,它将抛出。而且,当这种情况发生时我可以打电话给 conn.getResponseCode() 我得到的响应码是200。

    • 在服务器端,一个 EOFException 被扔进去 ObjectInputStream 的构造函数,该构造函数尝试读取序列化头,但由于客户端从未获取 OutputStream 写信给。

    如果有帮助的话,这里是 HttpsURLConnection 在呼叫之前 getOutputStream() (编辑后仅显示正在进行的调用,而不是执行此操作的代码的整个结构):

    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();
    

    问题是,我不知道这些事情怎么会发生,特别是考虑到它只会发生 偶尔地 (没有清晰的活动模式,我可以告诉)甚至只有当客户端和服务器之间有(相对的)高延迟时。

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

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

    1 回复  |  直到 14 年前
        1
  •  10
  •   JoseK    14 年前

    我们在一个与你相似的案例中发现了这些。通常在高负载下,不容易在测试中重现。还没有修好,但这是我们走过的步骤。

    如果是防火墙问题,我们将得到一个连接被拒绝或SocketTimeout异常。

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

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

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

    2) 网络管理团队查看TCP/IP通信量,以确定哪个端(或中间路由器)正在终止HTTP/TCP-IP会话。一旦我们了解到哪一端终止了连接,我们就要看看原因了。有足够知识的人可以跑 snoop

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

    4) 是否有可以丢弃请求的中间负载平衡器?

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

    5) 另一个建议是设置 ConnectTimeout 也不知道这些是否有更高的价值。 更新: 你可能想试试 conn.getErrorStream()

    如果 连接失败,但服务器已发送 但有用的数据。如果 连接未连接,或者如果 服务器在 连接或服务器是否有 错误但未发送错误数据,此 方法将返回null。

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

    更新: 到今天为止,我们学会了处理这个问题,因为我们合计每天400000个请求中有200-300个请求失败,这是0.00075%

        2
  •  0
  •   Lonzak    4 年前

    我们在服务器上使用它时也会遇到零星的超时。我们可以用两件事来解决它:

    1. 通过使用特定的ContentLength setFixedLengthStreamingMode (将错误率从大约150降低到10)
    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服务器的底层网络连接可能是 透明地被其他实例共享。

    所以现在打电话来 close() 只会没事,但也会打电话 disconnect() 将终止其他用户/透明共享连接的套接字,这些连接在达到超时时间后将运行到SocketTimeOut。