J'ai le projet, dans lequel je télécharge simultanément de nombreuses pages dans de nombreuses tâches, qui sont traitées via ThreadPool
(size = 200). Toutes ces tâches utilisent la même méthode getPage
pour le téléchargement de la page (avec Apache Commons HttpClient et Apache Commons IO):Se coincer à SocketInputStream.socketRead0
public static String getPage(String url)
throws IOException {
HttpUriRequest request = new HttpGet(url);
HttpResponse response = HTTP_CLIENT_BUILDER.build().execute(request);
try (InputStream content = response.getEntity().getContent()) {
return IOUtils.toString(content, "UTF-8");
}
}
tout HTTP_CLIENT_BUILDER
est un champ statique initialisé cette façon:
private static final HttpClientBuilder HTTP_CLIENT_BUILDER = HttpClients.custom()
.setDefaultRequestConfig(RequestConfig.custom()
.setSocketTimeout(SOCKET_TIMEOUT_MS) // 60_000
.setConnectTimeout(CONNECTION_TIMEOUT_MS) // 5_000
.build());
problème instruction: à un moment donné (lorsque la plupart des tâches sont terminées) tous les threads restants sont bloqués à la méthode native SocketInputStream.socketRead0
, donc jdb
dit, qu'ils sont tous en cours d'exécution (hmm, ouais, je m'attends à ce comportement avec natif rencontré Hod course :-)):
> threads
Group system:
(java.lang.ref.Reference$ReferenceHandler)0xac4 Reference Handler cond. waiting
(java.lang.ref.Finalizer$FinalizerThread)0xac5 Finalizer cond. waiting
(java.lang.Thread)0xac6 Signal Dispatcher running
(java.lang.Thread)0xac7 Java2D Disposer cond. waiting
Group main:
(java.lang.Thread)0xac9 pool-1-thread-5 running
(java.lang.Thread)0xaca pool-1-thread-12 running
(... 12 more threads from ThreadPool ...)
(java.lang.Thread)0xad7 DestroyJavaVM running
> where 0xac9
[1] java.net.SocketInputStream.socketRead0 (native method)
[2] java.net.SocketInputStream.read (SocketInputStream.java:150)
[3] java.net.SocketInputStream.read (SocketInputStream.java:121)
[4] sun.security.ssl.InputRecord.readFully (InputRecord.java:465)
[5] sun.security.ssl.InputRecord.read (InputRecord.java:503)
[6] sun.security.ssl.SSLSocketImpl.readRecord (SSLSocketImpl.java:961)
[7] sun.security.ssl.SSLSocketImpl.performInitialHandshake (SSLSocketImpl.java:1,363)
[8] sun.security.ssl.SSLSocketImpl.startHandshake (SSLSocketImpl.java:1,391)
[9] sun.security.ssl.SSLSocketImpl.startHandshake (SSLSocketImpl.java:1,375)
[10] org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket (SSLConnectionSocketFactory.java:275)
[11] org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket (SSLConnectionSocketFactory.java:254)
[12] org.apache.http.impl.conn.HttpClientConnectionOperator.connect (HttpClientConnectionOperator.java:117)
[13] org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect (PoolingHttpClientConnectionManager.java:314)
[14] org.apache.http.impl.execchain.MainClientExec.establishRoute (MainClientExec.java:363)
[15] org.apache.http.impl.execchain.MainClientExec.execute (MainClientExec.java:219)
[16] org.apache.http.impl.execchain.ProtocolExec.execute (ProtocolExec.java:195)
[17] org.apache.http.impl.execchain.RetryExec.execute (RetryExec.java:86)
[18] org.apache.http.impl.execchain.RedirectExec.execute (RedirectExec.java:108)
[19] org.apache.http.impl.client.InternalHttpClient.doExecute (InternalHttpClient.java:186)
[20] org.apache.http.impl.client.CloseableHttpClient.execute (CloseableHttpClient.java:82)
[21] org.apache.http.impl.client.CloseableHttpClient.execute (CloseableHttpClient.java:106)
[22] <package>.Utils.getPage (Utils.java:122)
[23...] <internal details>
> # the same picture for all of them
Je ne comprends pas, pourquoi cela peut arriver, mais je l'ai trouvé Java bug, qui est peut-être liée à la question. Alors peut-être que je ne cherche pas de solution réelle, mais pour une solution de contournement.
Étant donné que le bug est déposée contre Linux, je dois dire que je suis aussi en utilisant la machine virtuelle en cours d'exécution Ubuntu 14.04 x86_64
UPD: OK, ce que j'ai essayé est maintenant ajouter un nouveau délai d'attente avec setConnectionRequestTimeout
(juste pour se assurer, il ne fonctionne pas) ajouter finally
bloc getPage
sont acceptés dans les:
...
try (InputStream content = response.getEntity().getContent()) {
return IOUtils.toString(content, "UTF-8");
} finally {
httpClient.getConnectionManager().closeIdleConnections(0, TimeUnit.NANOSECONDS);
}
Voyons voir, si cela aide.
UPD2: cela semble aider un petit peu, mais encore, j'ai cette permanence des tâches en cours d'exécution coincé environ une fois par jour.
fil faisant 'Socket.read' sera montré comme' Runnable', voir ce SO après: http://stackoverflow.com/questions/12544212. Très probablement, le côté distant garde sa fin de socket ouverte, c'est pourquoi vos tâches ne peuvent pas finir. Par exemple, vous avez envoyé plus de tâches à un Executor que nécessaire pour télécharger des ressources distantes, et le reste des tâches inactives est laissé en attente. –
@VictorSorokin qui ne devrait pas se produire car j'ai défini des délais d'attente (voir initialiseur pour 'HTTP_CLIENT_BUILDER') –
Oui, négligé, désolé. Ensuite, j'examinerais la connexion avec tcpdump ou similaire pour comprendre ce qui maintient la connexion TCP en vie. Peut-être, les journaux côté serveur peuvent être utiles aussi. –