Java OKHTTP SocketTimeoutException并发多线程请求



我的Java应用程序使用各种代理服务器从特定域收集数据。特殊应用程序需要以下程序:

  • 通过指定代理加载URL
  • 等待5秒
  • 通过相同的代理加载下一个url

因此,信息的加载(由于5秒暂停)不会永远,我总共使用400个线程。每个线程都使用自己的代理服务器,也就是自己的OKHTTP客户端:

MyHTTPClient = new OkHttpClient.Builder()
.connectTimeout(7, TimeUnit.SECONDS)
.writeTimeout(7, TimeUnit.SECONDS)
.readTimeout(7, TimeUnit.SECONDS)
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ProxyIP, ProxyPort)))
.proxyAuthenticator((Route route, Response response) - > {
return response.request().newBuilder().header("Proxy-Authorization", Credentials.basic(ProxyUser, ProxyPass)).build();
})
.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
.hostnameVerifier((String hostname, SSLSession session) - > true)
.addNetworkInterceptor(new LoggingInterceptor())
.retryOnConnectionFailure(true)
.build();

每个线程都必须使用自己的代理,因此每个线程都有自己的OKHTTP客户端。总共有400个OKHTTP客户端。

每个线程现在按如下方式处理它的查询:

while (true) {
MyAnswer = MyHTTPClient.newCall(
new Request.Builder().url(https://www.example.com)
.addHeader("referer", SomeReferrer)
.addHeader("cache-control", "no-cache")
.addHeader("pragma", "no-cache")
.get().build())
.execute();
Body = MyAnswer.body().string();
MyAnswer.body().close();
Thread.sleep(5000);
}

这也很有效,大约一分钟。突然出现以下错误消息为每个请求每一个线程:

java.net.SocketTimeoutException: connect timed out
at java.base/java.net.PlainSocketImpl.waitForConnect(Native Method)
at java.base/java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:107)
at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399)
at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242)
at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.base/java.net.Socket.connect(Socket.java:608)
at okhttp3.internal.platform.Platform.connectSocket(Platform.kt:120)
at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.kt:295)
at okhttp3.internal.connection.RealConnection.connectTunnel(RealConnection.kt:261)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:201)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
Caused by: java.net.SocketTimeoutException: Read timed out
at java.base/java.net.SocketInputStream.socketRead0(Native Method)
at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:115)
at java.base/java.net.SocketInputStream.read(SocketInputStream.java:168)
at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
at okio.InputStreamSource.read(JvmOkio.kt:90)
at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:129)
... 22 more

WHAT I TRIED SO FAR

我做了一些研究,发现在并发连接方面可能存在某种限制。我发现,每当出现此错误时,连接池中没有空闲连接:

MyHTTPClient.connectionPool().idleConnectionCount() //Always 0 when the Timeout-Exception occurs

所以我试图增加每个okhttpclient连接池的大小,但这似乎绝对没有效果:

.connectionPool(new ConnectionPool(5000, 100, TimeUnit.MILLISECONDS))

我还尝试增加和减少httpclient的超时值,这实际上似乎对应用程序在没有此错误的情况下运行多长时间有影响,但它实际上并没有阻止这种情况发生。

我还尝试增加同时连接的数量,并减少连接在"time_wait"中停留的时间。此处提到的状态:https://learn.microsoft.com/en-us/answers/questions/482793/tcpip-cuncurrent-connections.html

我还尝试向OKHTTPClient添加Dispatcher,如下所示:

Dispatcher MyDispatcher = new Dispatcher(Executors.newCachedThreadPool());
MyDispatcher.setMaxRequests(9999999);
MyDispatcher.setMaxRequestsPerHost(9999999);

如果你有任何想法,可能是什么原因,或者我可以尝试什么,请告诉我。如果需要的话,我也很乐意给你发送更多的信息。

提前感谢!

首先创建一个OkHttpClient,然后使用OkHttpClient.newBuilder()自定义代理。这将导致客户机共享线程池和连接池。这可能不会解决你自己的问题,但它会使它更有效率。

final OkHttpClient sharedHttpClient = new OkHttpClient.Builder()
.connectTimeout(7, TimeUnit.SECONDS)
.writeTimeout(7, TimeUnit.SECONDS)
.readTimeout(7, TimeUnit.SECONDS)
.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
.hostnameVerifier((String hostname, SSLSession session) - > true)
.addNetworkInterceptor(new LoggingInterceptor())
.retryOnConnectionFailure(true)
.build();
MyHTTPClient = sharedHttpClient.newBuilder()
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ProxyIP, ProxyPort)))
.proxyAuthenticator((Route route, Response response) - > {
return response.request().newBuilder().header("Proxy-Authorization", Credentials.basic(ProxyUser, ProxyPass)).build();
})
.build();

我昨天遇到了类似的情况。以下是我对这些问题的看法:

  1. 第一步是找出发生了哪种超时。由于在httpclient中有几种类型的超时场景。如:
    • 从connectionPool获取连接超时
    • 建立套接字连接超时
    • 写和读操作超时

我有一个小技巧来实现这个目标:改变OkHttpClient.Builder()中的超时设置,让不同类型的超时彼此不同。然后在log中打印实际的超时时间,这样您就可以找到它真正匹配的时间。

  1. 检查tcp连接是否异常。由于您在程序中使用了连接池,因此它可能不会同时维护太多的连接。您可以通过以下命令查看连接数:
# each state of connection
netstat -n| awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
# connection to specifed port
netstat -ntap|grep :PORT
  1. 检查服务器端的响应时间。取决于你的web容器,你可以看到是否有任何请求需要很长时间才能返回。但这可能不是你的答案,因为你已经确认了另一台电脑没有同样的问题。

  2. 检查网络本身。使用此脚本检查是否有任何包花费太长时间(-w参数指定了超时时间)返回:

for i in {1..10000}; do nc -vnz IP PORT -w 1 2>&1 |grep "timed out"; done

就这些,希望对你有帮助。

相关内容

  • 没有找到相关文章

最新更新