running queda porque guarda funciona ejecutar compila como cambios java sockets http timeout apache-httpclient-4.x

queda - ¿Cómo evitar bloqueos en SocketInputStream.socketRead0 en Java?



porque no me funciona netbeans (7)

Realizar millones de solicitudes HTTP con diferentes bibliotecas de Java me da hilos colgados:

java.net.SocketInputStream.socketRead0()

Cuál es la función native

Traté de configurar Apche Http Client y RequestConfig para tener tiempos de espera en (espero) todo lo que es posible, pero aún así, tengo (probablemente infinito) cuelga en socketRead0 . ¿Cómo deshacerse de ellos?

La relación de bloqueo es aproximadamente ~ 1 por 10000 solicitudes (a 10000 hosts diferentes) y puede durar probablemente para siempre (he confirmado que el hilo colgado sigue siendo válido después de 10 horas).

JDK 1.8 en Windows 7.

Mi fábrica de HttpClient :

SocketConfig socketConfig = SocketConfig.custom() .setSoKeepAlive(false) .setSoLinger(1) .setSoReuseAddress(true) .setSoTimeout(5000) .setTcpNoDelay(true).build(); HttpClientBuilder builder = HttpClientBuilder.create(); builder.disableAutomaticRetries(); builder.disableContentCompression(); builder.disableCookieManagement(); builder.disableRedirectHandling(); builder.setConnectionReuseStrategy(new NoConnectionReuseStrategy()); builder.setDefaultSocketConfig(socketConfig); return HttpClientBuilder.create().build();

Mi fábrica RequestConfig :

HttpGet request = new HttpGet(url); RequestConfig config = RequestConfig.custom() .setCircularRedirectsAllowed(false) .setConnectionRequestTimeout(8000) .setConnectTimeout(4000) .setMaxRedirects(1) .setRedirectsEnabled(true) .setSocketTimeout(5000) .setStaleConnectionCheckEnabled(true).build(); request.setConfig(config); return new HttpGet(url);

OpenJDK socketRead0 fuente

Nota: En realidad tengo un "truco": puedo programar .getConnectionManager().shutdown() en otro Thread con la cancelación de Future si la solicitud finalizó correctamente, pero está depracated y también mata a HttpClient completo, no solo esa única solicitud.


Aunque esta pregunta menciona a Windows, tengo el mismo problema en Linux. Parece que hay un error en la forma en que JVM implementa los tiempos de espera del socket de bloqueo:

Para resumir, el tiempo de espera para bloquear sockets se implementa al llamar a poll en Linux (y select en Windows) para determinar que los datos estén disponibles antes de llamar a recv . Sin embargo, al menos en Linux, ambos métodos pueden indicar falsamente que los datos están disponibles cuando no lo están, lo que lleva al bloqueo de recv indefinidamente.

Desde la página de manual de encuesta (2) sección BUGS:

Consulte la discusión sobre notificaciones de disponibilidad espúrea en la sección BUGS de select (2).

Desde seleccionar (2) página de manual BUGS sección:

En Linux, select () puede informar un descriptor de archivo de socket como "listo para leer", mientras que una lectura posterior bloquea. Esto podría ocurrir, por ejemplo, cuando los datos han llegado, pero cuando el examen tiene una suma de comprobación incorrecta y se descarta. Puede haber otras circunstancias en las que un descriptor de archivo se informa falsamente como listo. Por lo tanto, puede ser más seguro usar O_NONBLOCK en sockets que no deberían bloquearse.

El código de Apache HTTP Client es un poco difícil de seguir, pero appears que la caducidad de la conexión solo está configurada para las conexiones HTTP keep-alive (que ha desactivado) y es indefinida a menos que el servidor especifique lo contrario. Por lo tanto, como lo señaló Oleg, el enfoque de la política de desalojo de Connection no funcionará en su caso y no se puede confiar en él en general.


Como dijo Clint , debería considerar un cliente HTTP sin bloqueo o (al ver que está utilizando Apache Httpclient) implementar una ejecución de solicitud multiproceso para evitar posibles bloqueos del hilo principal de la aplicación (esto no resuelve el problema, pero es mejor que reiniciar su aplicación porque está congelada). De todos modos, estableces la propiedad setStaleConnectionCheckEnabled , pero la verificación de conexión obsoleta no es 100% confiable, del tutorial de Apache Httpclient:

Una de las principales deficiencias del clásico modelo de E / S de bloqueo es que el zócalo de red puede reaccionar a los eventos de E / S solo cuando está bloqueado en una operación de E / S. Cuando se libera una conexión de nuevo al administrador, se puede mantener activa, pero no se puede monitorear el estado del socket y reaccionar ante cualquier evento de E / S. Si la conexión se cierra en el lado del servidor, la conexión del lado del cliente no puede detectar el cambio en el estado de la conexión (y reacciona de manera adecuada cerrando el socket en su extremo).

HttpClient intenta mitigar el problema probando si la conexión está "obsoleta", que ya no es válida porque se cerró por el lado del servidor, antes de usar la conexión para ejecutar una solicitud HTTP. La verificación de conexión obsoleta no es 100% confiable y agrega una sobrecarga de 10 a 30 ms a cada ejecución de solicitud.

El equipo de Apache HttpComponents recomienda la implementación de una política de desalojo de Connection

La única solución factible que no implica un modelo de subproceso único por socket para las conexiones inactivas es un subproceso de monitor dedicado utilizado para desalojar las conexiones que se consideran expiradas debido a un largo período de inactividad. El subproceso del monitor puede llamar periódicamente al método ClientConnectionManager # closeExpiredConnections () para cerrar todas las conexiones caducadas y desalojar las conexiones cerradas del grupo. También se puede llamar opcionalmente al método ClientConnectionManager # closeIdleConnections () para cerrar todas las conexiones que han estado inactivas durante un período de tiempo determinado.

Eche un vistazo al código de muestra de la sección Política de desalojo de Connection y trate de implementarlo en su aplicación junto con la ejecución de la solicitud Multithread, creo que la implementación de ambos mecanismos evitará los cuelgues no deseados.


Dado que nadie más respondió hasta ahora, aquí está mi opinión

Su configuración de tiempo de espera se ve perfectamente bien para mí. La razón por la cual ciertas solicitudes parecen estar constantemente bloqueadas en una llamada a java.net.SocketInputStream#socketRead0() es probable que se deba a una combinación de servidores que no funcionan bien y su configuración local. El tiempo de espera del socket define un período máximo de inactividad entre dos operaciones consecutivas de lectura de E / S (o en otras palabras, dos paquetes entrantes consecutivos). La configuración del tiempo de espera del socket es de 5,000 milisegundos. Siempre que el punto extremo opuesto continúe enviando un paquete cada 4,999 milisegundos para un mensaje codificado en un fragmento, la solicitud nunca java.net.SocketInputStream#socketRead0() y terminará bloqueando la mayor parte de su tiempo en java.net.SocketInputStream#socketRead0() . Puede averiguar si este es el caso ejecutando HttpClient con el registro de hilos encendido.


Debería considerar un cliente HTTP sin bloqueo como Grizzly o Netty que no tiene operaciones de bloqueo para colgar un hilo.


Me topé con el mismo problema usando el cliente http común de Apache.

Hay una solución bastante simple (que no requiere cerrar el administrador de conexión):

Necesitamos ejecutar la solicitud de la pregunta en un nuevo hilo con algunos gastos adicionales:

  • ejecutar la solicitud en un hilo separado, cerrar la solicitud y liberar su conexión en un hilo diferente, interrumpir el hilo suspendido
  • no ejecute EntityUtils.consumeQuietly(response.getEntity()) en el bloque finally (porque se cuelga en la conexión ''dead'')

Primero, agrega la interfaz

interface RequestDisposer { void dispose(); }

Ejecutar solicitudes http en un nuevo hilo

final AtomicReference<RequestDisposer> requestDisposer = new AtomicReference<>(null); final Thread thread = new Thread(() -> { final HttpGet request = new HttpGet("http://my.url"); final RequestDisposer disposer = () -> { request.abort(); request.releaseConnection(); }; requestDiposer.set(disposer); try (final CloseableHttpResponse response = httpClient.execute(request))) { ... } finally { disposer.dispose(); } };) thread.start()

Call dispose() en el hilo principal para cerrar la conexión colgante

requestDisposer.get().dispose(); // better check if it''s not null first thread.interrupt(); thread.join();

Eso solucionó el problema para mí.

Mi stacktrace se veía así:

java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:139) at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:155) at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:284) at org.apache.http.impl.io.ChunkedInputStream.getChunkSize(ChunkedInputStream.java:253) at org.apache.http.impl.io.ChunkedInputStream.nextChunk(ChunkedInputStream.java:227) at org.apache.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:186) at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:137) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)

A quien pueda resultarle interesante, reproduce fácilmente, interrumpe el hilo sin abortar la solicitud y la liberación de la conexión (la relación es aproximadamente 1/100). Windows 10, versión 10.0. jdk8.151-x64.


Para Apache HTTP Client (bloqueo) encontré que la mejor solución es getConnectionManager (). y apagarlo.

Entonces, en una solución de alta confiabilidad, simplemente programo el cierre en otro hilo y, en caso de que la solicitud no se complete, me estoy cerrando desde otro hilo


Tengo más de 50 máquinas que generan aproximadamente 200k solicitudes / día / máquina. Están ejecutando Amazon Linux AMI 2017.03. Anteriormente tenía jdk1.8.0_102, ahora tengo jdk1.8.0_131. Estoy usando tanto apacheHttpClient como OKHttp como bibliotecas raspantes.

Cada máquina ejecutaba 50 hilos y, a veces, los hilos se pierden. Después de perfilar con el perfilador de perfiles de youkit java obtuve

ScraperThread42 State: RUNNABLE CPU usage on sample: 0ms java.net.SocketInputStream.socketRead0(FileDescriptor, byte[], int, int, int) SocketInputStream.java (native) java.net.SocketInputStream.socketRead(FileDescriptor, byte[], int, int, int) SocketInputStream.java:116 java.net.SocketInputStream.read(byte[], int, int, int) SocketInputStream.java:171 java.net.SocketInputStream.read(byte[], int, int) SocketInputStream.java:141 okio.Okio$2.read(Buffer, long) Okio.java:139 okio.AsyncTimeout$2.read(Buffer, long) AsyncTimeout.java:211 okio.RealBufferedSource.indexOf(byte, long) RealBufferedSource.java:306 okio.RealBufferedSource.indexOf(byte) RealBufferedSource.java:300 okio.RealBufferedSource.readUtf8LineStrict() RealBufferedSource.java:196 okhttp3.internal.http1.Http1Codec.readResponse() Http1Codec.java:191 okhttp3.internal.connection.RealConnection.createTunnel(int, int, Request, HttpUrl) RealConnection.java:303 okhttp3.internal.connection.RealConnection.buildTunneledConnection(int, int, int, ConnectionSpecSelector) RealConnection.java:156 okhttp3.internal.connection.RealConnection.connect(int, int, int, List, boolean) RealConnection.java:112 okhttp3.internal.connection.StreamAllocation.findConnection(int, int, int, boolean) StreamAllocation.java:193 okhttp3.internal.connection.StreamAllocation.findHealthyConnection(int, int, int, boolean, boolean) StreamAllocation.java:129 okhttp3.internal.connection.StreamAllocation.newStream(OkHttpClient, boolean) StreamAllocation.java:98 okhttp3.internal.connection.ConnectInterceptor.intercept(Interceptor$Chain) ConnectInterceptor.java:42 okhttp3.internal.http.RealInterceptorChain.proceed(Request, StreamAllocation, HttpCodec, Connection) RealInterceptorChain.java:92 okhttp3.internal.http.RealInterceptorChain.proceed(Request) RealInterceptorChain.java:67 okhttp3.internal.http.BridgeInterceptor.intercept(Interceptor$Chain) BridgeInterceptor.java:93 okhttp3.internal.http.RealInterceptorChain.proceed(Request, StreamAllocation, HttpCodec, Connection) RealInterceptorChain.java:92 okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(Interceptor$Chain) RetryAndFollowUpInterceptor.java:124 okhttp3.internal.http.RealInterceptorChain.proceed(Request, StreamAllocation, HttpCodec, Connection) RealInterceptorChain.java:92 okhttp3.internal.http.RealInterceptorChain.proceed(Request) RealInterceptorChain.java:67 okhttp3.RealCall.getResponseWithInterceptorChain() RealCall.java:198 okhttp3.RealCall.execute() RealCall.java:83

Descubrí que tienen una solución para esto

https://bugs.openjdk.java.net/browse/JDK-8172578

en JDK 8u152 (acceso anticipado). Lo he instalado en una de nuestras máquinas. Ahora estoy esperando ver algunos buenos resultados.