1. 程式人生 > >HttpClient大併發下Timeout waiting for connection from pool 問題解決方案

HttpClient大併發下Timeout waiting for connection from pool 問題解決方案

錯誤:org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool

前言  :

第一次看到這個錯誤, 上網找了下,有文章說是連線池不夠了。。。。   並沒有多想,立即將原有程式的 連結池擴容了3倍,然後單個路由  擴容了5倍。

問題解決, 以為找到了,答案。 但是 過了大約 幾天之後,再次出現該問題,當時就特別疑惑, 沒有擴容之前 程式已經運行了 將近兩年,並沒有發生任何錯誤,

現在 擴容了,竟然還報這個錯, 此時 分析方向  應該發生改變。 

懷疑方向一 :   大量的Timeout 出現,是否 請求的域名有問題 ? 

 於是,手動 ping 了訪問域名,發現並沒有多慢.    (疑問否決)

懷疑方向二 :  是否我們伺服器出口IP 有問題, 請求 指定域名 超時? 

於是:  我們新開了一臺機器, 然後將 原有的兩臺機器 停掉一臺, 並且新開的機器  使用 全新的出口IP, 以區分於原有機器,後來發現 新卡的機器,沒有問題。

因此,我們大致定位了問題,就是 伺服器的出口IP 有問題, 並不是程式的問題。 所以,解決方案就是 找運維 重新申請配置了機器.

下面 貼出 我連線池的 寫法, 做個小筆記 。

HttpClient  版本如下 

<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.3.4</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpcore</artifactId>
			<version>4.3.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpmime</artifactId>
			<version>4.3.1</version>
		</dependency>
連線池程式碼如下 :
package com.**.weixin.common.https;

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;


public class HttpClientFactory {

    private static final Integer MAX_TOTAL = 300;             //連線池最大連線數
    private static final Integer MAX_PER_ROUTE = 50;          //單個路由預設最大連線數
    private static final Integer REQ_TIMEOUT =  5 * 1000;     //請求超時時間ms
    private static final Integer CONN_TIMEOUT = 5 * 1000;     //連線超時時間ms
    private static final Integer SOCK_TIMEOUT = 10 * 1000;    //讀取超時時間ms
    private static HttpClientConnectionMonitorThread thread;  //HTTP連結管理器執行緒

    public static HttpClientConnectionMonitorThread getThread() {
		return thread;
	}
	public static void setThread(HttpClientConnectionMonitorThread thread) {
		HttpClientFactory.thread = thread;
	}

	public static HttpClient createSimpleHttpClient(){
		SSLConnectionSocketFactory sf = SSLConnectionSocketFactory.getSocketFactory();
		return HttpClientBuilder.create()
		        .setSSLSocketFactory(sf)
		        .build();
    }
    
	public static HttpClient createHttpClient() {
		PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
		poolingHttpClientConnectionManager.setMaxTotal(MAX_TOTAL);
		poolingHttpClientConnectionManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
		RequestConfig requestConfig = RequestConfig.custom()
				.setConnectionRequestTimeout(REQ_TIMEOUT)
				.setConnectTimeout(CONN_TIMEOUT).setSocketTimeout(SOCK_TIMEOUT)
				.build();
		HttpClientFactory.thread=new HttpClientConnectionMonitorThread(poolingHttpClientConnectionManager); //管理 http連線池
		return HttpClients.custom().setConnectionManager(poolingHttpClientConnectionManager).setDefaultRequestConfig(requestConfig).build();
	}
}
以下 執行緒用來清理 連線池無效的連結 :
package com.**.weixin.common.https;

import java.util.concurrent.TimeUnit;

import org.apache.http.conn.HttpClientConnectionManager;

/**
 * <p>Description: 使用管理器,管理HTTP連線池 無效連結定期清理功能</p> 
 * @author andy 2017年8月28日
 */
public class HttpClientConnectionMonitorThread extends Thread {

	private final HttpClientConnectionManager connManager;
	private volatile boolean shutdown;

	public HttpClientConnectionMonitorThread(HttpClientConnectionManager connManager) {
		super();
		this.setName("http-connection-monitor");
		this.setDaemon(true);
		this.connManager = connManager;
		this.start();
	}

	@Override
	public void run() {
		try {
			while (!shutdown) {
				synchronized (this) {
					wait(5000); // 等待5秒
					// 關閉過期的連結
					connManager.closeExpiredConnections();
					// 選擇關閉 空閒30秒的連結
					connManager.closeIdleConnections(30, TimeUnit.SECONDS);
				}
			}
		} catch (InterruptedException ex) {
		}
	}
	
	/**
	 * 方法描述: 停止 管理器 清理無效連結  (該方法當前暫時關閉) 
	 * @author andy 2017年8月28日 下午1:45:18
	 */
	@Deprecated
	public void shutDownMonitor() {
		synchronized (this) {
			shutdown = true;
			notifyAll();
		}
	}

}