异常信息
org.apache.http.NoHttpResponseException xxx failed to respond
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:141)
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
其实这个异常是偶发的,就是因为偶发所以不太好查,主要是本地不好复现,只在服务器压测时有问题
上网找了些解答,但说的其实不太清楚,最后再stackoverflow找到答案了
解决方案其实在官方文档里也有解答 http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html
主要是这段
2.5. Connection eviction policy
One of the major shortcomings of the classic blocking I/O model is that the network socket can react to I/O events only when blocked in an I/O operation. When a connection is released back to the manager, it can be kept alive however it is unable to monitor the status of the socket and react to any I/O events. If the connection gets closed on the server side, the client side connection is unable to detect the change in the connection state (and react appropriately by closing the socket on its end).
HttpClient tries to mitigate the problem by testing whether the connection is 'stale', that is no longer valid because it was closed on the server side, prior to using the connection for executing an HTTP request. The stale connection check is not 100% reliable. The only feasible solution that does not involve a one thread per socket model for idle connections is a dedicated monitor thread used to evict connections that are considered expired due to a long period of inactivity. The monitor thread can periodically call
ClientConnectionManager#closeExpiredConnections()
method to close all expired connections and evict closed connections from the pool. It can also optionally callClientConnectionManager#closeIdleConnections()
method to close all connections that have been idle over a given period of time.
这个异常的原因就是客户端创建了到服务端的连接,但是服务端会在连接空闲时关闭这个连接,而HttpClient无法感知这个事件,所以继续使用这个链接就报错了,毕竟那边都断了
Most likely persistent connections that are kept alive by the connection manager become stale. That is, the target server shuts down the connection on its end without HttpClient being able to react to that event, while the connection is being idle, thus rendering the connection half-closed or 'stale'.
解决方案其实就是配置两个参数,使用httpclient的话基本都会用到线程池
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.closeExpiredConnections();
//这里的空闲时间要和keepalive配合使用,比如keepalive配置20s,那这里的关闭空闲时间最好配置15s
connectionManager.closeIdleConnections(closeIdleTime, TimeUnit.SECONDS);
connectionManager.closeExpiredConnections(); 关闭过期链接
connectionManager.closeIdleConnections(closeIdleTime, TimeUnit.SECONDS); 隔多久关闭空闲状态的链接