本身封裝了一個請求類,因爲本身寫的時候太自覺得是,致使多線程請求的時候報錯 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,有沒有大牛有好辦法解決?