java什么可能导致socket连接异常:连接超时?
我们有一个Webstart客户端,它通过使用java.net.HttpsURLConnection
通过HTTPS发送序列化对象来与服务器通信
在我的本地机器和我们办公室的测试服务器上,一切都运行得非常好,但我遇到了一个非常奇怪的问题,它只发生在我们的生产和暂存服务器上(偶尔也会发生)。据我所知,这些服务器与我们办公室中的服务器之间的主要区别在于,它们位于其他地方,与它们的客户机-服务器通信速度相当慢,但在此之前,它在生产中运行了很长一段时间
不管怎样,现在发生的事情如下:
- 在设置选项(如读取超时)和属性(如
HttpURLConnection
上的Content-Type
)后,客户机调用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
,而HttpURLConnection
上的connect()
方法表示,如果超时在建立连接之前过期,它会抛出这个SocketTimeoutException
。而且,当这种情况发生时,我可以调用conn.getResponseCode()
,得到200的响应代码
- 在服务器端,
EOFException
在ObjectInputStream
的构造函数中抛出一个EOFException
,该构造函数试图读取序列化头,但失败了,因为客户端从未获得要写入的OutputStream
李>
如果有帮助,下面是在调用getOutputStream()
之前对HttpsURLConnection
进行的调用(编辑为仅显示正在进行的调用,而不是执行此操作的代码的整个结构):
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 楼答案
在服务器上使用它时,我们也会偶尔出现超时。我们可以通过两件事来解决这个问题:
setFixedLengthStreamingMode
使用特定的ContentLength(将错误率从~150降低到10)伪代码:
发生这种情况的另一种理论可能是:
在HttpURLConnection/HttpsURLConnection的文档中,可以阅读以下内容:
因此,现在只调用
close()
就可以了,但同时调用disconnect()
将终止其他用户/透明共享连接的套接字,在达到超时时间后,这些连接将运行到SocketTimeOut# 2 楼答案
我们在一个与你类似的案例中遇到了这些。通常在高负载下,不容易在测试中重现。我们还没有修复,但这是我们所经历的步骤
如果是防火墙问题,我们会得到连接被拒绝或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()
6)还可以尝试在服务器上每隔5秒进行一组线程转储,以查看是否有线程在服务器上显示这些传入请求
更新:从今天起,我们学会了面对这个问题,因为我们将每天400000个请求中的失败率合计为200-300个,即0.00075%