java.lang.IllegalStateException: Connection pool shut down

最近使用HttpClient 4.5 使用 CloseableHttpClient 發起鏈接後,使用CloseableHttpResponse 接受返回結果,結果就報錯了,上網查了下,有位stackoverflow的大兄弟說,只要將:html

 CloseableHttpClient httpClient    = HttpClients.createDefault();
改成:
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connManager).setConnectionManagerShared(true).build();

就能夠整正常執行了,因而,用之,果真不報錯了,可是爲何呢?如下是大兄弟的原文解釋:

I was having a similar error when I came across this thread and this seemed to fix the issue for me. I know this is an old question, but adding thoughts for others for future reference.java

I'm not 100% sure as to why this fix works as the documentation around this is pretty awful. It was a lot of trial and error with what I was testing to get to this solution. From what I canapache

gather though, this fix works because it is then using a shared connection pool in the background, which means that connections remain open for use.編程

關鍵是最後一句話:大概意思是,後臺使用一個共享鏈接池,供剩下打開的鏈接去使用安全

 

原文地址:https://stackoverflow.com/questions/41744410/executorservice-performing-rest-requests服務器

感謝下這位大兄弟多線程

 

apache 官方的建議是,建立鏈接池,併爲每個接口URL分配一個線程,去執行,還給出了許多高併發訪問的編碼技巧併發

原文:https://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.htmlsocket

那麼,使用HttpClient 4.5鏈接池的正確姿式是什麼呢?高併發

原做者地址:https://my.oschina.net/xlj44400/blog/711341

 

摘要: HttpClient 是 Apache Jakarta Common 下的子項目,能夠用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,而且它支持 HTTP 協議最新的版本和建議

HttpClient簡介

HttpClient 是 Apache Jakarta Common 下的子項目,能夠用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,而且它支持 HTTP 協議最新的版本和建議。HttpClient支持的功能以下:

  • 支持Http0.九、Http1.0和Http1.1協議。
  • 實現了Http所有的方法(GET,POST,PUT,HEAD 等)。
  • 支持HTTPS協議。
  • 支持代理服務器。
  • 提供安全認證方案。
  • 提供鏈接池以便重用鏈接。
  • 鏈接管理器支持多線程應用。支持設置最大鏈接數,同時支持設置每一個主機的最大鏈接數,發現並關閉過時的鏈接。
  • 在http1.0和http1.1中利用KeepAlive保持長鏈接。

之前是commons-httpclient,後面被Apache HttpComponents取代,目前版本4.5.x,咱們如今用的就是4.5版本

HttpClient鏈接池使用

爲何要用Http鏈接池:

一、下降延遲:若是不採用鏈接池,每次鏈接發起Http請求的時候都會從新創建TCP鏈接(經歷3次握手),用完就會關閉鏈接(4次揮手),若是採用鏈接池則減小了這部分時間損耗

二、支持更大的併發:若是不採用鏈接池,每次鏈接都會打開一個端口,在大併發的狀況下系統的端口資源很快就會被用完,致使沒法創建新的鏈接
  • 默認http協議:
private static final Charset CHAR_SET = Charset.forName("utf-8");
private static PoolingHttpClientConnectionManager cm;

	public void init() {
		cm = new PoolingHttpClientConnectionManager();
		cm.setMaxTotal(50);
		cm.setDefaultConnectionConfig(ConnectionConfig.custom()
				.setCharset(CHAR_SET).build());
		SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(30000)
				.setSoReuseAddress(true).build();
		cm.setDefaultSocketConfig(socketConfig);
		// HttpProtocolParams.setContentCharset(httpParams, "UTF-8");
		// HttpClientParams.setCookiePolicy(httpParams, "ignoreCookies");
		// HttpConnectionParams.setConnectionTimeout(httpParams, 30000);
		// HttpConnectionParams.setSoTimeout(httpParams, 30000);
		httpClient = HttpClientBuilder.create().setConnectionManager(cm)
				.build();
	}

	public CloseableHttpClient getHttpClient() {
		int timeout=2;
		RequestConfig config = RequestConfig.custom()
				.setConnectTimeout(timeout * 1000) //設置鏈接超時時間,單位毫秒
				//.setConnectionRequestTimeout(timeout * 1000) //設置從connect Manager獲取Connection 超時時間,單位毫秒
				.setSocketTimeout(timeout * 1000).build(); //請求獲取數據的超時時間,單位毫秒
		CloseableHttpClient _httpClient = HttpClients.custom()
				.setConnectionManager(cm).setDefaultRequestConfig(config)
				.build();
		if(cm!=null&&cm.getTotalStats()!=null) { //打印鏈接池的狀態		
			LOGGER.info("now client pool {}",cm.getTotalStats().toString());
		}
		return _httpClient;
	}

	public String post(String url, Map<String, String> params) {
		HttpPost post = new HttpPost(url);
		String resp = null;
		try {
			if(params != null){
				List<NameValuePair> nvps = new ArrayList<NameValuePair>();
				for (Map.Entry<String, String> param : params.entrySet()) {
					nvps.add(new BasicNameValuePair(param.getKey(), param.getValue()));
				}
				post.setEntity(new UrlEncodedFormEntity(nvps, CHAR_SET));
			}
			
			try {
				HttpResponse response = httpClient.execute(post);
				InputStream input = response.getEntity().getContent();
				resp = IOUtils.toString(input);
			} catch (ClientProtocolException e) {
				LOGGER.error(e.getMessage(), e);
			} catch (IOException e) {
				LOGGER.error(e.getMessage(), e);
			} catch (Exception e) {
				LOGGER.error(e.getMessage(), e);
			}
		} finally {
			if (post != null)
				post.releaseConnection();
		}
		return resp;
	}
  • https協議:
public class HttpConnectionManager {

    PoolingHttpClientConnectionManager cm = null;
    
    public void init() {
        LayeredConnectionSocketFactory sslsf = null;
        try {
            sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
                .register("https", sslsf)
                .register("http", new PlainConnectionSocketFactory())
                .build();
        cm =new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        cm.setMaxTotal(200);
        cm.setDefaultMaxPerRoute(20);
    }

    public CloseableHttpClient getHttpClient() {       
        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(cm)
                .build();          
        
        /*
        //若是不採用鏈接池就是這種方式獲取鏈接
        CloseableHttpClient httpClient = HttpClients.createDefault();
        */
        return httpClient;
    }
}
  • httpClient使用
catch (Exception e) {
            logger.error("ufile send error e:",e);
            try {
                if (resEntity != null && resEntity.getContent() != null) {
                    resEntity.getContent().close();
                }
            } catch (IllegalStateException | IOException e1) {
                logger.error("ufile send error e1:",e1);
            } finally {
                if (getMethod!=null) {
                    getMethod.releaseConnection();
                }
                /*if (httpClient!=null) { //鏈接池使用的時候不能關閉鏈接,不然下次使用會拋異常 java.lang.IllegalStateException: Connection pool shut down
                    try {
                        httpClient.close();
                    } catch (IOException e2) {
                        logger.error("ufile httpclient close error e2:",e2);
                    }
                }*/
            }
        }
  • 鏈接池使用注意事項:
    1. 鏈接池中鏈接都是在發起請求的時候創建,而且都是長鏈接
    
    2. HttpResponse input.close();做用就是將用完的鏈接釋放,下次請求能夠複用,這裏特別注意的是,若是不使用in.close();而僅僅使用httpClient.close();結果就是鏈接會被關閉,而且不能被複用,這樣就失去了採用鏈接池的意義。
    
    3. 鏈接池釋放鏈接的時候,並不會直接對TCP鏈接的狀態有任何改變,只是維護了兩個Set,leased和avaliabled,leased表明被佔用的鏈接集合,avaliabled表明可用的鏈接的集合,釋放鏈接的時候僅僅是將鏈接從leased中remove掉了,並把鏈接放到avaliabled集合中

打印的狀態:

INFO c.m.p.u.h.HttpClientUtils[72] - now client pool [leased: 0; pending: 0; available: 0; max: 50]

leased :the number of persistent connections tracked by the connection manager currently being used to execute requests.  
  
available :the number idle persistent connections.  
  
pending : the number of connection requests being blocked awaiting a free connection.  
  
max: the maximum number of allowed persistent connections.

HttpClient 4.5超時設置

4.5版本中,這兩個參數的設置都抽象到了RequestConfig中,由相應的Builder構建,具體的例子以下:

CloseableHttpClient httpclient = HttpClients.createDefault();  
HttpGet httpGet = new HttpGet("http://stackoverflow.com/");  
RequestConfig requestConfig = RequestConfig.custom()  
      .setConnectTimeout(5000).setConnectionRequestTimeout(1000)  
      .setSocketTimeout(5000).build();  
httpGet.setConfig(requestConfig);  
CloseableHttpResponse response = httpclient.execute(httpGet);  
System.out.println("獲得的結果:" + response.getStatusLine());//獲得請求結果  
HttpEntity entity = response.getEntity();//獲得請求回來的數據
  • setConnectTimeout:設置鏈接超時時間,單位毫秒。ConnectTimeoutException
  • setConnectionRequestTimeout:設置從connect Manager獲取Connection 超時時間,單位毫秒。這個屬性是新加的屬性,由於目前版本是能夠共享鏈接池的。ConnectionPoolTimeout
  • setSocketTimeout:請求獲取數據的超時時間,單位毫秒。 若是訪問一個接口,多少時間內沒法返回數據,就直接放棄這次調用。SocketTimeoutException
  • 上面3個時間4.5版本默認是-1,就是不限,若是不設置就會一直等待
相關文章
相關標籤/搜索