httpclient4 多線程請求RetryExec I/O exception 問題解決

本身封裝了一個請求類,因爲本身寫的時候太自覺得是,致使多線程請求的時候報錯 java

18:33 INFO  [o.a.h.i.e.RetryExec] I/O exception (java.net.SocketException) caught when processing request to {}->http://172.10.10.xxx:xxxx: Socket Closed web

18:33 INFO  [o.a.h.i.e.RetryExec] Retrying request to {}->http://172.10.10.xxx:xxxx spring

還有這樣的錯誤 安全

18:33 ERROR [c.w.p.e.ExceptionMapper] ERROR:500,[PLATFORM]Connection is not open 多線程

18:33 ERROR [c.w.p.e.ExceptionMapper] java.lang.IllegalStateException: Connection is not open 併發

private static PoolingHttpClientConnectionManager cm;
    private static CloseableHttpClient httpClient = null;
    private static HttpGet get = null;

    static {
        initManager();
    }

    private static void initManager() {
        cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(100);
        cm.setDefaultMaxPerRoute(2);
        HttpHost localhost = new HttpHost(PropertyUtil.getProperty("recommend.host"), PropertyUtil.getInteger("recommend.port"));
        cm.setMaxPerRoute(new HttpRoute(localhost), 100);
        httpClient = HttpClients.custom().setConnectionManager(cm).build();
        get = new HttpGet();
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(1000)
                .setConnectTimeout(1000)
                .build();
        get.setConfig(requestConfig);
    }

    public static String getRecommend(URI uri) {
        if (httpClient != null && get != null) {
            try {
                get.setURI(uri);  //注意這裏
                CloseableHttpResponse response = httpClient.execute(get);
                get.releaseConnection();
                return EntityUtils.toString(response.getEntity(), "utf-8");
            } catch (IOException e) {
                throw new IllegalRequestException(ErrorMessageUtil.RECOMMEND_TIMEOUT);
            }
        } else {
            System.out.println("Error httpClient or get is null!");
        }
        return "";
    }

這是最開始的寫法,在項目中保證httpclient和get都只生成一個實例,由於是多線程請求,正在作優化,對返回效率敏感,想到建立對象時損耗比較大就使用這種方式,後來就報了上面的2種錯誤 app

首先要說這個錯誤的引發是因爲多線程的關係,因爲我以前寫過一個多線程請求的方法,因此我認爲在web項目中也能夠這樣調用 優化

當我解決了這個問題再回去看那個代碼的時候我才發現,爲何那個沒問題,由於在以前的代碼裏,我指定了一個類繼承自Thread,在這個thread裏我new了一個HttpGet,因此在這個線程裏保證了get只有一個,而在web項目中,spring初始化的時候就new出了一個HttpGet,並且是多線程複用的,這時候就出現了問題 ui

解決方法也很簡單: spa

private static PoolingHttpClientConnectionManager cm;
    private static CloseableHttpClient httpClient = null;
    private static RequestConfig requestConfig = null;

    static {
        initManager();
    }

    private static void initManager() {
        cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(100);
        cm.setDefaultMaxPerRoute(2);
        HttpHost localhost = new HttpHost(PropertyUtil.getProperty("recommend.host"), PropertyUtil.getInteger("recommend.port"));
        cm.setMaxPerRoute(new HttpRoute(localhost), 100);
        httpClient = HttpClients.custom().setConnectionManager(cm).build();
        requestConfig = RequestConfig.custom().setSocketTimeout(1000).setConnectTimeout(1000).build();
    }

    public static String getRecommend(URI uri) {
        try {
            HttpGet get = new HttpGet();  //就改這裏
            get.setConfig(requestConfig);
            get.setURI(uri);
            CloseableHttpResponse response = httpClient.execute(get);
            get.releaseConnection();
            return EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (ClientProtocolException e) {
            System.out.println("ClientProtocolException: " + uri.toString());
        } catch (IOException e) {
            throw new IllegalRequestException(ErrorMessageUtil.RECOMMEND_TIMEOUT);
        }
        return "";
    }

這樣我每次調用的時候直接new出一個新的HttpGet請求,就完事了,併發數調高了一倍都沒有再報錯

太自覺得是就是由於我查看了PoolingHttpClientConnectionManager和CloseableHttpClient,都是線程安全的,結果忽略的HttpGet,根本就沒考慮這個請求的線程安全問題,明明源碼裏都標明瞭

@NotThreadSafe
public class HttpGet extends HttpRequestBase


備忘下,httpclient裏繼承自HttpRequestBase的Method都是非線程安全的

ps:多線程裏每次還要new個新的HttpGet,有沒有大牛有好辦法解決?

相關文章
相關標籤/搜索