Apache HTTP Components 5.1设置Socket超时仍触发SocketTimeoutException
Apache HTTP Components 5.1 SocketTimeoutException 问题排查
我在项目中使用Apache HTTP Components 5.1版本,遇到了SocketTimeoutException问题。参照Apache HTTP Client 4.5.13的方式,通过org.apache.hc.client5.http.config.RequestConfig.setSocketTimeout方法设置了5分钟的超时时间,但问题依旧存在。
我的配置代码
import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; RequestConfig config = RequestConfig.custom() .setConnectTimeout(Timeout.ofMinutes(5)) .setConnectionRequestTimeout(Timeout.ofMinutes(5)) .setSocketTimeout(Timeout.ofMinutes(5)).build(); CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultCookieStore(cookieStore) .setDefaultRequestConfig(config) .build();
报错信息(已翻译)
Caused by: java.net.SocketTimeoutException: 读取超时 at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:283) at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:309) at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:350) at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:803) at java.base/java.net.Socket$SocketInputStream.read(Socket.java:966) at org.apache.hc.core5.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:149) at org.apache.hc.core5.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:280) at org.apache.hc.core5.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:241) at org.apache.hc.core5.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:53) at org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:298) at org.apache.hc.core5.http.impl.io.HttpRequestExecutor.execute(HttpRequestExecutor.java:175) at org.apache.hc.core5.http.impl.io.HttpRequestExecutor.execute(HttpRequestExecutor.java:218) at org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager$InternalConnectionEndpoint.execute(PoolingHttpClientConnectionManager.java:583) at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.execute(InternalExecRuntime.java:212) at org.apache.hc.client5.http.impl.classic.MainClientExec.execute(MainClientExec.java:105) at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51) at org.apache.hc.client5.http.impl.classic.ExecChainElement$1.proceed(ExecChainElement.java:57) at org.apache.hc.client5.http.impl.classic.ConnectExec.execute(ConnectExec.java:182) at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51) at org.apache.hc.client5.http.impl.classic.ExecChainElement$1.proceed(ExecChainElement.java:57) at org.apache.hc.client5.http.impl.classic.ProtocolExec.execute(ProtocolExec.java:175) at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51) at org.apache.hc.client5.http.impl.classic.ExecChainElement$1.proceed(ExecChainElement.java:57) at org.apache.hc.client5.http.impl.classic.HttpRequestRetryExec.execute(HttpRequestRetryExec.java:96) at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51) at org.apache.hc.client5.http.impl.classic.ExecChainElement$1.proceed(ExecChainElement.java:57) at org.apache.hc.client5.http.impl.classic.ContentCompressionExec.execute(ContentCompressionExec.java:133) at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51) at org.apache.hc.client5.http.impl.classic.ExecChainElement$1.proceed(ExecChainElement.java:57) at org.apache.hc.client5.http.impl.classic.RedirectExec.execute(RedirectExec.java:115) at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51) at org.apache.hc.client5.http.impl.classic.InternalHttpClient.doExecute(InternalHttpClient.java:170) at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:75)
排查与解决思路
- 检查请求级配置:如果发起请求时单独传入了
RequestConfig,会覆盖客户端默认配置。确认所有请求都没有单独设置超时,或者确保请求级的SocketTimeout也配置正确。 - 连接池复用验证:连接池中的旧连接可能未应用新超时配置。可以重启应用,或者配置连接池的闲置验证参数,让连接复用前自动校验:
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); // 闲置5秒后复用前验证连接有效性 connectionManager.setValidateAfterInactivity(Timeout.ofSeconds(5)); CloseableHttpClient httpClient = HttpClientBuilder.create() .setConnectionManager(connectionManager) .setDefaultRequestConfig(config) .build(); - 区分读取超时与总响应超时:5.x版本中
SocketTimeout是两次数据读取的间隔超时,而非整个响应的总时间。如果服务器分块传输且间隔较长,可添加总响应超时配置:RequestConfig config = RequestConfig.custom() .setConnectTimeout(Timeout.ofMinutes(5)) .setConnectionRequestTimeout(Timeout.ofMinutes(5)) .setSocketTimeout(Timeout.ofMinutes(5)) .setResponseTimeout(Timeout.ofMinutes(10)) // 整个请求响应的总超时 .build(); - 确认服务器状态:排查服务器是否存在响应缓慢、资源过载的情况,这也可能导致客户端读取超时。
内容的提问来源于stack exchange,提问作者Gayan Buddhika
相关产品推荐
相关产品推荐

