最近使用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支持的功能以下:
之前是commons-httpclient,后面被Apache HttpComponents取代,目前版本4.5.x,咱们如今用的就是4.5版本
为何要用Http链接池:
一、下降延迟:若是不采用链接池,每次链接发起Http请求的时候都会从新创建TCP链接(经历3次握手),用完就会关闭链接(4次挥手),若是采用链接池则减小了这部分时间损耗 二、支持更大的并发:若是不采用链接池,每次链接都会打开一个端口,在大并发的状况下系统的端口资源很快就会被用完,致使没法创建新的链接
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; }
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; } }
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.
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();//获得请求回来的数据
ConnectTimeoutException
ConnectionPoolTimeout
SocketTimeoutException