简体   繁体   English

并发多线程请求的 Java OKHTTP SocketTimeoutException

[英]Java OKHTTP SocketTimeoutException with concurrent multithreaded requests

My Java application uses a variety of proxy servers to collect data from a particular domain.我的 Java 应用程序使用各种代理服务器从特定域收集数据。 The special application requires the following procedure:特殊申请需要以下程序:

  • Loading a URL through a specific proxy通过特定代理加载 URL
  • Wait 5 seconds等待 5 秒
  • Loading the next url through the same proxy通过同一个代理加载下一个 url

So that the loading of the information (due to the 5 second pause) doesn't take forever, I work with a total of 400 threads.为了使信息的加载(由于 5 秒暂停)不会永远持续,我总共使用了 400 个线程。 Each of these threads uses its own proxy server, ie also with its own OKHTTP client:这些线程中的每一个都使用自己的代理服务器,即也使用自己的 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();

Each thread has to use its own proxy, so each thread has its own OKHTTP client.每个线程都必须使用自己的代理,因此每个线程都有自己的 OKHTTP 客户端。 In total there are 400 OKHTTP clients.总共有 400 个 OKHTTP 客户端。

Each thread now processes its queries as follows:每个线程现在按如下方式处理其查询:

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

And that works great too, for about a minute.这也很有效,大约一分钟。 Then suddenly the following error messages appear for each individual request on every single thread:然后突然为每个线程上的每个单独请求出现以下错误消息:

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我到目前为止所做的尝试

I've done a bit of research and found out that there might be some kind of limit in terms of concurrent connections.我做了一些研究,发现在并发连接方面可能存在某种限制。 I found out, that whenever this error occurs, there are no idle connections in the connection pool:我发现,每当发生此错误时,连接池中都没有空闲连接:

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

So i tried to increase the size of the connection pool for every single okhttpclient but that seemed to have absolutely no effect:因此,我尝试为每个 okhttpclient 增加连接池的大小,但这似乎完全没有效果:

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

I also tried to increase and decrease the timeout-values of the httpclient and that actually seemed to make a difference in terms of how long the application would run without this error but it actually didn't prevent this to happen.我还尝试增加和减少 httpclient 的超时值,这实际上似乎对应用程序在没有此错误的情况下运行的时间产生了影响,但实际上并没有阻止这种情况的发生。

I also tried to increase the amount of simultaneous connections and decrease the amount of time a connection stays in "TIME_WAIT" status as mentioned here: https://docs.microsoft.com/en-us/answers/questions/482793/tcpip-cuncurrent-connections.html我还尝试增加同时连接的数量并减少连接保持在“TIME_WAIT”状态的时间,如下所述: https : //docs.microsoft.com/en-us/answers/questions/482793/tcpip- cuncurrent-connections.html

I also tried to add a Dispatcher to the OKHTTPClient, as follows:我也尝试在OKHTTPClient中添加一个Dispatcher,如下:

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

If you have any ideas about what could be the cause or what else I could try, please let me know.如果您对可能的原因或我可以尝试的其他方法有任何想法,请告诉我。 I am also happy to send you further information, if needed.如果需要,我也很乐意向您发送更多信息。

Thank you in advance!先感谢您!

Create one OkHttpClient initially and then use OkHttpClient.newBuilder() to customize the proxy.首先创建一个 OkHttpClient,然后使用OkHttpClient.newBuilder()自定义代理。 This will cause the clients to share thread pools and connection pools.这将导致客户端共享线程池和连接池。 That might not fix your problem on its own, but it'll make it more efficient.这本身可能无法解决您的问题,但会使其更有效率。

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

I have met a similar situation last day.我昨天遇到了类似的情况。 Here are my ideas about these problem:以下是我对这些问题的看法:

  1. The first step is to find out which kind of timeout you have occurred.第一步是找出您发生了哪种超时。 Since there are several types of timeout scene in httpclient.由于httpclient有几种超时场景。 Such as:如:
    • timeout to fetch a connection from connectionPool从 connectionPool 获取连接的超时时间
    • timeout to establish a socket connection建立套接字连接超时
    • timeout to do wite and read operation超时执行 wite 和 read 操作

I have a little trick to accomplish this goal: Change the settings of timeout in OkHttpClient.Builder() , let different kind of timeout vary from each one others.我有一个小技巧来实现这个目标:更改OkHttpClient.Builder()的超时设置,让不同类型的超时彼此不同。 And then print the actual timeout duration in log, so you can find out which one it truely match.然后在日志中打印实际超时持续时间,以便您可以找出它真正匹配的那个。

  1. Check if there is any abnormal tcp connection.检查tcp连接是否有异常。 Since you have use connection pool in your program, it may not mantains too much connections at the same time.由于您在程序中使用了连接池,因此它可能不会同时维护太多连接。 You can check the number of connection by:您可以通过以下方式检查连接数:
# 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. Check the response time in server side.检查服务器端的响应时间。 Depends on your web container, you can see if there is any request take too long to return.取决于您的 Web 容器,您可以查看是否有任何请求需要太长时间才能返回。 But it may not be your answer, since you have confirm another computor doesn't have the same issue.但这可能不是您的答案,因为您已确认另一台计算机没有同样的问题。

  2. Check the network itself.检查网络本身。 Use this script to check if any package take too long( -w param specified the timeout duration) to return:使用此脚本检查是否有任何包花费太长时间( -w param 指定超时持续时间)返回:

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

That's all, hope that may help you.就这些,希望能帮到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM