两个主机建立连接的过程是非常复杂的一个过程,涉及到多个数据包的交换,而且也非常耗时间。Http连接须要的三次握手开销非常大,这一开销对于比較小的http消息来说更大。但是假设我们直接使用已经建立好的http连接。这样花费就比較小。吞吐率更大。

传统的HttpURLConnection并不支持连接池。假设要实现连接池的机制,还须要自己来管理连接对象。对于网络请求这种底层相对复杂的操作。个人以为假设有可用的其它方案,也没有必要自己去管理连接对象。

除了HttpURLConnection,大家肯定还知道HttpClient。普通情况下,普通使用HttpClient已经能满足我们的需求,只是有时候。在我们须要高并发大量的请求网络的时候,还是用“连接池”这种概念能提升吞吐量。

我们来看下怎么使用 org.apache.httpcomponents.httpclient(版本号4.4)提供的连接池来实现我们的高并发网络请求。

使用到的jar包:

org\apache\httpcomponents\httpclient\4.4-beta1\httpclient-4.4-beta1.jar

org\apache\httpcomponents\httpclient-cache\4.4-beta1\httpclient-cache-4.4-beta1.jar

org\apache\httpcomponents\httpcore\4.4-beta1\httpcore-4.4-beta1.jar

以下代码实例中主要使用到 PoolingHttpClientConnectionManager

package com.hvgroup.zhuhai10086.jms.utils;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException; import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils; /**
* HttpClient工具类
*
* @return
* @author SHANHY
* @create 2015年12月18日
*/
public class HttpClientUtil { static final int timeOut = 10 * 1000; private static CloseableHttpClient httpClient = null; private final static Object syncLock = new Object(); private static void config(HttpRequestBase httpRequestBase) {
// 设置Header等
// httpRequestBase.setHeader("User-Agent", "Mozilla/5.0");
// httpRequestBase
// .setHeader("Accept",
// "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
// httpRequestBase.setHeader("Accept-Language",
// "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");// "en-US,en;q=0.5");
// httpRequestBase.setHeader("Accept-Charset",
// "ISO-8859-1,utf-8,gbk,gb2312;q=0.7,*;q=0.7"); // 配置请求的超时设置
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(timeOut)
.setConnectTimeout(timeOut).setSocketTimeout(timeOut).build();
httpRequestBase.setConfig(requestConfig);
} /**
* 获取HttpClient对象
*
* @return
* @author SHANHY
* @create 2015年12月18日
*/
public static CloseableHttpClient getHttpClient(String url) {
String hostname = url.split("/")[2];
int port = 80;
if (hostname.contains(":")) {
String[] arr = hostname.split(":");
hostname = arr[0];
port = Integer.parseInt(arr[1]);
}
if (httpClient == null) {
synchronized (syncLock) {
if (httpClient == null) {
httpClient = createHttpClient(200, 40, 100, hostname, port);
}
}
}
return httpClient;
} /**
* 创建HttpClient对象
*
* @return
* @author SHANHY
* @create 2015年12月18日
*/
public static CloseableHttpClient createHttpClient(int maxTotal,
int maxPerRoute, int maxRoute, String hostname, int port) {
ConnectionSocketFactory plainsf = PlainConnectionSocketFactory
.getSocketFactory();
LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory
.getSocketFactory();
Registry<ConnectionSocketFactory> registry = RegistryBuilder
.<ConnectionSocketFactory> create().register("http", plainsf)
.register("https", sslsf).build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(
registry);
// 将最大连接数添加
cm.setMaxTotal(maxTotal);
// 将每一个路由基础的连接添加
cm.setDefaultMaxPerRoute(maxPerRoute);
HttpHost httpHost = new HttpHost(hostname, port);
// 将目标主机的最大连接数添加
cm.setMaxPerRoute(new HttpRoute(httpHost), maxRoute); // 请求重试处理
HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
public boolean retryRequest(IOException exception,
int executionCount, HttpContext context) {
if (executionCount >= 5) {// 假设已经重试了5次,就放弃
return false;
}
if (exception instanceof NoHttpResponseException) {// 假设server丢掉了连接。那么就重试
return true;
}
if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
return false;
}
if (exception instanceof InterruptedIOException) {// 超时
return false;
}
if (exception instanceof UnknownHostException) {// 目标server不可达
return false;
}
if (exception instanceof ConnectTimeoutException) {// 连接被拒绝
return false;
}
if (exception instanceof SSLException) {// SSL握手异常
return false;
} HttpClientContext clientContext = HttpClientContext
.adapt(context);
HttpRequest request = clientContext.getRequest();
// 假设请求是幂等的,就再次尝试
if (!(request instanceof HttpEntityEnclosingRequest)) {
return true;
}
return false;
}
}; CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.setRetryHandler(httpRequestRetryHandler).build(); return httpClient;
} private static void setPostParams(HttpPost httpost,
Map<String, Object> params) {
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
Set<String> keySet = params.keySet();
for (String key : keySet) {
nvps.add(new BasicNameValuePair(key, params.get(key).toString()));
}
try {
httpost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} /**
* GET请求URL获取内容
*
* @param url
* @return
* @author SHANHY
* @throws IOException
* @create 2015年12月18日
*/
public static String post(String url, Map<String, Object> params) throws IOException {
HttpPost httppost = new HttpPost(url);
config(httppost);
setPostParams(httppost, params);
CloseableHttpResponse response = null;
try {
response = getHttpClient(url).execute(httppost,
HttpClientContext.create());
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, "utf-8");
EntityUtils.consume(entity);
return result;
} catch (Exception e) {
// e.printStackTrace();
throw e;
} finally {
try {
if (response != null)
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} /**
* GET请求URL获取内容
*
* @param url
* @return
* @author SHANHY
* @create 2015年12月18日
*/
public static String get(String url) {
HttpGet httpget = new HttpGet(url);
config(httpget);
CloseableHttpResponse response = null;
try {
response = getHttpClient(url).execute(httpget,
HttpClientContext.create());
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, "utf-8");
EntityUtils.consume(entity);
return result;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (response != null)
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
} public static void main(String[] args) {
// URL列表数组
String[] urisToGet = {
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497", "http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497", "http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497", "http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497", "http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497", "http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497",
"http://blog.csdn.net/catoop/article/details/38849497" }; long start = System.currentTimeMillis();
try {
int pagecount = urisToGet.length;
ExecutorService executors = Executors.newFixedThreadPool(pagecount);
CountDownLatch countDownLatch = new CountDownLatch(pagecount);
for (int i = 0; i < pagecount; i++) {
HttpGet httpget = new HttpGet(urisToGet[i]);
config(httpget);
// 启动线程抓取
executors
.execute(new GetRunnable(urisToGet[i], countDownLatch));
}
countDownLatch.await();
executors.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("线程" + Thread.currentThread().getName() + ","
+ System.currentTimeMillis() + ", 全部线程已完毕。開始进入下一步! ");
} long end = System.currentTimeMillis();
System.out.println("consume -> " + (end - start));
} static class GetRunnable implements Runnable {
private CountDownLatch countDownLatch;
private String url; public GetRunnable(String url, CountDownLatch countDownLatch) {
this.url = url;
this.countDownLatch = countDownLatch;
} @Override
public void run() {
try {
System.out.println(HttpClientUtil.get(url));
} finally {
countDownLatch.countDown();
}
}
}
}

Post用法

// 当中 params 为 Map<String, Object> params
String ret = HttpClientUtil.post(url, params);
jsonRet = new JSONObject(ret);

一開始我是使用传统的 HttpURLConnection 来做网络请求的。查了非常多资料,有不少说 HttpURLConnection 效率高的。但是经过我改动实现方法后,HttpClient 连接池版本号的网络请求相对照较稳定。

这也说明,我们并不请尽信他人讲解,凡事还是要寻找适合自己的方法,真正的解决自己的问题,才是王道。

===========================================

在使用 HttpURLConnection 的时候,大并发对外做网络请求的时候,前期请求耗时还好。后面耗时越来越高。以下是我之前的实现代码:

    @Deprecated
protected JSONObject callRestfulOld(String url, Map<String, Object> params)
{
String temp;
String ret="";
JSONObject jsonRet=null;
String sign = generateSign("POST", url, params);// 对參数进行加密签名
if(sign.isEmpty()) return new JSONObject("{\"ret_code\":-1,\"err_msg\":\"generateSign error\"}");
params.put("sign", sign);
try{
URL u = new URL(url);
HttpURLConnection conn = (HttpURLConnection)u.openConnection();
conn.setRequestMethod("POST");
conn.setConnectTimeout(10000);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
StringBuffer param = new StringBuffer();
for (String key: params.keySet())
{
param.append(key).append("=").append(URLEncoder.encode(params.get(key).toString(), "UTF-8")).append("&");
}
conn.getOutputStream().write(param.toString().getBytes("UTF-8")); //System.out.println(param);
conn.getOutputStream().flush();
conn.getOutputStream().close();
InputStreamReader isr = new InputStreamReader(conn.getInputStream());
BufferedReader br = new BufferedReader(isr);
while((temp = br.readLine()) != null){
ret += temp;
}
br.close();
isr.close();
conn.disconnect();
//System.out.println(ret);
jsonRet = new JSONObject(ret); } catch(java.net.SocketTimeoutException e) {
//e.printStackTrace();
jsonRet = new JSONObject("{\"ret_code\":-1,\"err_msg\":\"call restful timeout\"}");
} catch(Exception e) {
//e.printStackTrace();
jsonRet = new JSONObject("{\"ret_code\":-1,\"err_msg\":\"call restful error\"}");
}
return jsonRet;
}

(完)

Http请求连接池 - HttpClient 的 PoolingHttpClientConnectionManager的更多相关文章

  1. Http请求连接池-HttpClient的AbstractConnPool源码分析

    在做服务化拆分的时候,若不是性能要求特别高的场景,我们一般对外暴露Http服务.Spring里提供了一个模板类RestTemplate,通过配置RestTemplate,我们可以快速地访问外部的Htt ...

  2. Http持久连接与HttpClient连接池

    一.背景 HTTP协议是无状态的协议,即每一次请求都是互相独立的.因此它的最初实现是,每一个http请求都会打开一个tcp socket连接,当交互完毕后会关闭这个连接. HTTP协议是全双工的协议, ...

  3. Http 持久连接与 HttpClient 连接池

    一.背景 HTTP协议是无状态的协议,即每一次请求都是互相独立的.因此它的最初实现是,每一个http请求都会打开一个tcp socket连接,当交互完毕后会关闭这个连接. HTTP协议是全双工的协议, ...

  4. httpclient httpclient使用连接池

    httpclient使用连接池 http协议是无状态的,但毕竟是基于tcp的,底层还是需要和服务器连接的, 对于需要从同一个站点抓取大量网页的程序,应该使用连接池,否则每次抓取都和web站点建立连接, ...

  5. Nginx 如何通过连接池处理网络请求

    L:35-36 worker_connections 默认 512个 这个链接需要设置的  worker_cpu_affinity 0001 0010 0100 1000;关联CPU connecti ...

  6. 带有连接池的Http客户端工具类HttpClientUtil

    一.背景 业务开发中,经常会遇到通过http/https向下游服务发送请求.每次都要重复造轮子写HttpClient的逻辑,而且性能.功能参差不齐.这里分享一个高性能的.带连接池的通用Http客户端工 ...

  7. nodejs的mysql模块学习(六)连接池的创建和使用

    介绍 在 软件工程 , 连接池 是一个 高速缓存 的 数据库连接 维持,使得连接可以当需要将来向数据库请求重复使用. [ 来源请求 ] 连接池用于提高数据库上执行命令的性能. 打开并保持每个用户的数据 ...

  8. Druid连接池配置全攻略

    Druid是阿里开源出来的数据库连接池,性能非常好,还自带日志监控. 它的DataSource类为:com.alibaba.druid.pool.DruidDataSource. 由于使用的yaml格 ...

  9. httpclient: 设置连接池及超时配置,请求数据:PoolingHttpClientConnectionManager

    public static void main(String[] args) throws Exception{ //httpclient连接池 //创建连接池 PoolingHttpClientCo ...

随机推荐

  1. Material Design控件使用学习 TabLayout+SwipeRefreshlayout

    效果: Tablayout有点类似之前接触过的开源ViewPagerIndicator,将其与viewpager绑定,可实现viewpager的导航功能. SwipeRefreshLayout是官方出 ...

  2. Servlet doPost方法同时上传图片和传递参数

    上传图片和传递参数 上传图片和文件属于enctype="multipart/form-data"  form中加入enctype="multipart/form-data ...

  3. Sub Thread to update main Thread (UI) 2

    Sub Thread to update main Thread (UI)  2 Handler.post(somethread); Handler.sendMessage("Msg&quo ...

  4. Android开发当中Parcelable接口的使用

    本文转载于:http://www.2cto.com/kf/201205/132814.html 本文稍微做了些修改 android提供了一种新的类型:Parcel.本类被用作封装数据的容器,封装后的数 ...

  5. flex 光标(CursorManager)

    flex 光标(CursorManager)  CursorManager相关属性   getInstance():ICursorManager AIR 应用程序中的每个 mx.core.Window ...

  6. LG Gram 2018 z980 白

    因为今年8代处理器i5的双核变成了四核,感觉是个换电脑的好时机,本来打算买macbook,但是6月的发布会并没有发布,于是开始寻找一些比较有特点的笔记本电脑. 了解了这样一款笔记本 LG GRAM 1 ...

  7. 【js基础】判断是否是合法邮箱地址(正则表达式的应用)

    2019-01-21 09:11:21 <!DOCTYPE html> <html> <head> <meta charset="utf-8&quo ...

  8. chmod---变更文件或目录的权限

    chmod命令用来变更文件或目录的权限.在UNIX系统家族里,文件或目录权限的控制分别以读取.写入.执行3种一般权限来区分,另有3种特殊权限可供运用.用户可以使用chmod指令去变更文件与目录的权限, ...

  9. Mysql学习总结(19)——Mysql无法创建外键的原因

    在MySQL中创建外键时,经常会遇到问题而失败,这是因为Mysql中还有很多细节需要我们去留意,我自己总结并查阅资料后列出了以下几种常见原因. 1.  两个字段的类型或者大小不严格匹配.例如,如果一个 ...

  10. pix格式的一些摸索

    作者:朱金灿 来源:http://blog.csdn.net/clever101 以前因为工作关系研究过PCI的系统格式pix,但是遗留了一些问题,最近又想重新解决这些问题.研究了一天,有些收获,但是 ...