java httppost超时时间 - 如何防止Java中的SocketInputStream.socketRead0挂起?




4 Answers

虽然这个问题提到了Windows,但我在Linux上遇到了同样的问题。 看来JVM实现阻塞套接字超时的方式有一个缺陷:

总而言之,阻塞套接字的超时是通过在Linux上调用poll (并在Windows上select )来确定数据在调用recv之前可用来实现的。 但是,至少在Linux上,这两种方法都可以虚假地指示数据在不存在时可用,从而无限期地导致recv阻塞。

来自poll(2)手册页BUGS部分:

请参阅select(2)的BUGS部分下的虚假就绪通知的讨论。

从select(2)手册页BUGS部分:

在Linux下,select()可以将套接字文件描述符报告为“准备好读取”,而不是后续的读取块。 这可能例如在数据到达时发生但在检查时具有错误的校验和并被丢弃。 可能存在其他情况,其中虚假地报告文件描述符为就绪。 因此,在不应阻塞的套接字上使用O_NONBLOCK可能更安全。

Apache HTTP客户端代码有点难以理解,但appears只为HTTP保持活动连接(已禁用)设置了连接到期,并且除非服务器另行指定,否则它是无限期的。 因此,正如oleg所指出的, 连接驱逐策略方法在您的情况下不起作用,并且一般不能依赖。

httpclient timeout

使用不同的Java库执行数百万个HTTP请求会让我挂起线程:

java.net.SocketInputStream.socketRead0()

这是native功能。

我试图设置Apche Http Client和RequestConfig以便在(我希望)everythig上超时,但仍然, 我有(可能是无限的)挂起socketRead0 如何摆脱它们?

挂起比率约为每10000个请求约1个(到10000个不同的主机)并且它可能永远持续(我已经确认线程挂起仍然有效,10小时后仍然有效)。

Windows 7上的JDK 1.8。

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

我的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源码

注意:实际上我有一些“技巧” - 如果请求正确完成,我可以在其他Thread安排.getConnectionManager().shutdown()取消Future ,但是它被删除并且它也会杀死整个HttpClient ,而不仅仅是那个请求。




我有超过50台机器,每天可处理约20万件机器/机器。 他们正在运行Amazon Linux AMI 2017.03。 我以前有jdk1.8.0_102,现在我有jdk1.8.0_131。 我使用apacheHttpClient和OKHttp作为抓取库。

每台机器运行50个线程,有时线程会丢失。 在使用Youkit java profiler进行分析后,我得到了

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

我发现他们有一个解决方案

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

在JDK 8u152(早期访问)。 我已将它安装在我们的一台机器上。 现在我等着看到一些好的结果。




对于Apache HTTP Client(阻塞),我发现最好的解决方案是getConnectionManager()。 并关闭它。

所以在高可靠性的解决方案中,我只是在其他线程中安排关闭,以防万一请求没有完成我正在关闭其他线程




鉴于到目前为止没有其他人做出回应,这是我的看法

您的超时设置对我来说完全没问题。 某些请求似乎在java.net.SocketInputStream#socketRead0()调用中经常被阻止的原因可能是由于行为不当的服务器和本地配置的组合。 套接字超时定义了两个连续的i / o读取操作(或换句话说,两个连续的传入数据包)之间的最大不活动时间段。 套接字超时设置为5,000毫秒。 只要对端点继续为块编码消息每隔4,999毫秒发送一个数据包,请求就永远不会超时,最终会在java.net.SocketInputStream#socketRead0()阻止大部分时间被阻塞。 您可以通过运行打开有线记录的HttpClient来查明是否是这种情况。




Related