HttpClient引發的執行緒數過多導致應用崩潰

菜農一號發表於2020-10-18

[背景]
應用中執行緒數量 一直在增加,dump執行緒日誌發現大量IdleConnectionEvictor日誌。在這裡插入圖片描述
原始碼分析:

    //evictExpiredConnections  這個配置作用:
				//設定一個定時執行緒,定時清理閒置連線,可以將這個定時時間設定為 keep alive timeout 時間的一半以保證超時前回收
                //所以在build httpclinet 的時候可以設定evictExpiredConnections()
              
            if (evictExpiredConnections || evictIdleConnections) {
              //這裡要看IdleConnectionEvictor原始碼,裡面會建立一個執行緒
                final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm,
                        maxIdleTime > 0 ? maxIdleTime : 10, maxIdleTimeUnit != null ? maxIdleTimeUnit : TimeUnit.SECONDS,
                        maxIdleTime, maxIdleTimeUnit);
                closeablesCopy.add(new Closeable() {

                    @Override
                    public void close() throws IOException {
                        connectionEvictor.shutdown();
                        try {
                            connectionEvictor.awaitTermination(1L, TimeUnit.SECONDS);
                        } catch (final InterruptedException interrupted) {
                            Thread.currentThread().interrupt();
                        }
                    }

                });
                connectionEvictor.start();//這裡會建立evaictor執行緒
            }

所以每建立一個httpclient就會建立一個evictor執行緒,如果不設定連線超時時間和空閒執行緒回收的話這些執行緒會一直存在;
解決方法:
建立httpClient連線池,自定義connectManager

public class HttpClientFactory {
    private CloseableHttpClient httpClient;

    public void  construct(){
        RequestConfig config = RequestConfig.custom()
                .setSocketTimeout(10000)//請求獲取資料的超時時間(即響應時間),單位毫秒
                .setConnectTimeout(10000)//設定連線超時時間,單位毫秒
                .setConnectionRequestTimeout(500)//設定從connect Manager(連線池)獲取Connection 超時時間,單位毫秒
                .build();

        //設定連線存活時間,總的最大連線數和每個路由的最大連線數
        PoolingHttpClientConnectionManager poolHttpConnManager = new PoolingHttpClientConnectionManager(60, TimeUnit.SECONDS);
        poolHttpConnManager.setMaxTotal(60);
        poolHttpConnManager.setDefaultMaxPerRoute(20);

        //初始化client
        HttpClientBuilder httpBuilder = HttpClients.custom()
                .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
                .setDefaultRequestConfig(config)
                .setConnectionManager(poolHttpConnManager)
                .evictExpiredConnections() 
                //MaxIdleTime 必須小於服務端的關閉時間否則有可能出現NoHttpResponse
                .evictIdleConnections(5,TimeUnit.SECONDS);//用來關閉閒置連線,它會啟動一個守護執行緒進行清理工作。使用者可以通過builder#evictIdleConnections開啟該元件,並通過builder#setmaxIdleTime設定最大空閒時間。

        httpClient=httpBuilder.build();
    }

    public void destroy() throws Exception{
        if(null != this.httpClient){
            this.httpClient.close();
        }
    }
}

相關文章