Apache HttpClient 没有设置time out导致应用长时间阻塞的问题

本文介绍了如何解决Apache HttpClient在HTTP请求中出现的阻塞问题,通过设置合理的超时参数来避免长时间等待,确保应用程序的稳定运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

update 2014-5-18:

今天又处理了一个HttpClient阻塞的问题,还是socket read没有超时设置。

用jstack -l pid 得到线程的调用栈,每隔一段时间执行一次,对比三次的thread dump结果,发现有一个线程是三次执行的位置一样的,说明它是阻塞在这里了:

"org.springframework.jms.listener.DefaultMessageListenerContainer#7-1" prio=10 tid=0x00007f345127d800 nid=0x5b4f0 runnable [0x00007f34753d1000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.read(SocketInputStream.java:150)
	at java.net.SocketInputStream.read(SocketInputStream.java:121)
	at org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer(AbstractSessionInputBuffer.java:130)
	at org.apache.http.impl.io.SocketInputBuffer.fillBuffer(SocketInputBuffer.java:127)
	at org.apache.http.impl.io.AbstractSessionInputBuffer.readLine(AbstractSessionInputBuffer.java:233)
	at org.apache.http.impl.io.ChunkedInputStream.getChunkSize(ChunkedInputStream.java:220)
	at org.apache.http.impl.io.ChunkedInputStream.nextChunk(ChunkedInputStream.java:183)
	at org.apache.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:152)
	at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:138)
	at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
	at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
	at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
	- locked <0x000000070346ce70> (a java.io.InputStreamReader)
	at java.io.InputStreamReader.read(InputStreamReader.java:184)
	at java.io.Reader.read(Reader.java:140)
	at org.apache.http.util.EntityUtils.toString(EntityUtils.java:161)

业务的逻辑是接收一个JMS消息,再http请求调用得到处理结果。在http请求处阻塞了,导致后面的消息都不能消息。

----------------------------------------------

现在的对外接口一般都是Http + json的,因为简单,语言无关。

Apache HttpClient应该是最常用的Java http组件了。这货有个坑爹的地方,Apache HttpClient如果对方不回应,或者网络原因不返回了,那么HttpClient会一直阻塞。这种情况在公网可能比较容易碰到。在内网时,也有一次因为一台中转的nginx挂掉而导致hessian请求长时间阻塞。

因为Http Client默认的SO_TIMEOUT是0,即一直等待。

这个问题,在帮同事查找问题时碰到好几次了,可能是大家潜意识里认为Http请求是即时的,失败的话也很快返回。


Apache HttpClient的示例也没提到要设置TimeOut,这也是比较坑爹的地方。一个库如果没有默认阻止用户去范错误,那么你也应当在文档,示例代码里提醒用户不要范错误。

有三个可以设置time out 的参数:

httpClient = new DefaultHttpClient();
httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_LINGER, value)
httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 3000);
httpClient.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 3000);

SO_LINGER最好不要设置,可能会坑死人。参考:

http://unliminet.blog.51cto.com/380895/346686

http://stackoverflow.com/questions/3757289/tcp-option-so-linger-zero-when-its-required


### Java HttpClient 连接池配置示例与最佳实践 #### 配置连接池 为了优化 HTTP 请求处理性能并减少资源消耗,在使用 `HttpClient` 时通常会启用连接池机制。通过合理设置连接池参数可以显著提升应用程序的响应速度和吞吐量。 ```java import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(); connMgr.setMaxTotal(200); // 设置最大连接数 connMgr.setDefaultMaxPerRoute(20); // 设置默认每路由的最大并发数 RequestConfig requestConfig = RequestConfig.custom() .setSocketTimeout(5000) // Socket 超时时间 (ms) .setConnectTimeout(5000) // 建立连接超时时间 (ms) .setConnectionRequestTimeout(5000) // 获取连接等待时间 (ms) .build(); CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(connMgr) .setDefaultRequestConfig(requestConfig) .build(); ``` 此代码片段展示了如何创建一个带有自定义连接管理器和服务端请求配置的 `HttpClient` 实例[^1]。 #### 最佳实践建议 - **保持合理的连接数量**:根据应用的实际需求调整最大总连接数 (`setMaxTotal`) 和单个主机的最大并发连接数 (`setDefaultMaxPerRoute`),避免过多闲置连接占用系统资源。 - **适当设置超时参数**:对于网络不稳定的情况,应设定合适的 socket timeout, connect timeout 及 connection request timeout 参数来防止长时间阻塞操作影响整体性能。 - **重用客户端实例**:尽可能重复利用同一个 `HttpClient` 对象而不是频繁创建新的实例,因为每次新建都会重新初始化底层 TCP/IP 协议栈中的套接字对象,造成不必要的开销。 - **关闭不再使用的连接**:当某个特定目标服务器上的所有活动都结束之后应当及时释放对应的连接以便其他线程能够继续使用这些宝贵的资源;可以通过调用 `httpClient.close()` 方法显式地关闭整个 client 或者依靠 JVM 的垃圾回收机制自动清理未被引用的对象。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值