为什么使用HTTP连接池?

随着系统架构风格逐渐向前后端分离架构,微服务架构转变,RestFul风格API的开发与设计,同时SpringMVC也很好的支持了REST风格接口。各个系统之间服务的调用大多采用HTTP+JSONHTTPS+JSON方式。
HTTP1.1默认是持久连接,HTTP1.0也可以通过在请求头中设置Connection:keep-alive使得连接成为长连接。既然HTTP协议支持长连接,那么HTTP连接同样可以使用连接池技术来管理和维护连接建立和销毁。 但是由于每次HTTP连接请求实际上都是在传输层建立的TCP连接,利用的socket进行通信,HTTP连接的保持和关闭实际上都同TCP连接的建立和关闭有关,所有每次HTTP请求都有经过TCP连接的三次握手(建立连接)和四次挥手(释放连接)的过程。所以采用HTTP连接池有以下优势:

  • 降低了频繁建立HTTP连接的时间开销,减少了TCP连接建立和释放时socket通信服务器端资源的浪费;
  • 支持更高的并发量;

常用HttpClient连接池API

本文使用的是目前最新版本的HttpClient4.5.3,所以下文的内容都是基于该版本书写。

  • PoolingHttpClientConnectionManager连接池管理实现类
    PoolingHttpClientConnectionManager是一个HttpClient连接池实现类,实现了HttpClientConnectionManagerConnPoolControl接口。类层次结构如下图所示:
 
PoolingHttpClientConnectionManager类层次结构

构造方法:
PoolingHttpClientConnectionManager():无参构造方法,从源码中可以看到该方法调用了getDefaultRegistry()来注册http协议和https协议。
常用方法:
public void setMaxTotal(int max):该方法定义在ConnPoolControl接口中,表示设置最大连接数为max。
public void setDefaultMaxPerRoute(int max):该方法也是定义在ConnPoolControl接口中,表示将每个路由的默认最大连接数设置为max
public void setMaxPerRoute(HttpRoute route,int max):设置某个指定路由的最大连接数,这个配置会覆盖setDefaultMaxPerRoute的某个路由的值。

  • RequestConfig请求参数配置类
 
RequestConfig方法与内部类Builder

常用方法
static RequestConfig.Builder custom():静态方法,用于构建Builder 对象,然后设置相应的参数;
int getConnectionRequestTimeout():获取从连接池获取连接的最长时间,单位是毫秒;
int getConnectTimeout():获取创建连接的最长时间,单位是毫秒;
int getSocketTimeout():获取数据传输的最长时间,单位是毫秒;

RequestConfig有一个静态内部类Builder,用于构建RequestConfig对象并设置请求参数,该类有以下常用方法:
public RequestConfig.Builder setConnectionRequestTimeout(int connectionRequestTimeout):设置从连接池获取连接的最长时间,单位是毫秒;
public RequestConfig.Builder setConnectTimeout(int connectTimeout):设置创建连接的最长时间,单位是毫秒;
public RequestConfig.Builder setSocketTimeout(int socketTimeout):设置数据传输的最长时间,单位是毫秒;

  • HttpRequestRetryHandler请求重试接口
    boolean retryRequest(IOException exception, int executionCount, org.apache.http.protocol.HttpContext context):实现该接口的,必须实现该方法,决定了如果一个方法执行时发生了IO异常,是否应该重试,重试executionCount次。

单线程-使用连接池管理HTTP请求

主要步骤:

  1. 创建HTTP的连接池管理对象cm,如下所示
  1. PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

2.设置最大连接数和每个路由的默认最大连接数等参数,如下所示

  1. //将最大连接数增加到200
  2. cm.setMaxTotal(200);
  3. //将每个路由的默认最大连接数增加到20
  4. cm.setDefaultMaxPerRoute(20);

3.模拟发送HttpGet请求或者HttpPost请求,注意这里创建HttpClients对象的时候需要从连接池中获取,即要设置连接池对象,当执行发生IO异常需要处理时,要实现HttpRequestRetryHandler接口;需要注意的是这里处理完请求响应后,不能关闭HttpClient对象,如果关闭连接池也会销毁。HttpClients对象创建对象所示:

  1. CloseableHttpClient httpClient = HttpClients.custom()
  2. .setConnectionManager(connectionManager)
  3. .setRetryHandler(retryHandler(5)).build();

4.可以开启线程来监听连接池中空闲连接,并清理无效连接,线程监听频率可以自行设置。
Java实现源码:

  1. package com.liangpj.develop.httpclient;
  2. import org.apache.http.HttpEntityEnclosingRequest;
  3. import org.apache.http.HttpRequest;
  4. import org.apache.http.NoHttpResponseException;
  5. import org.apache.http.client.HttpRequestRetryHandler;
  6. import org.apache.http.client.config.RequestConfig;
  7. import org.apache.http.client.methods.CloseableHttpResponse;
  8. import org.apache.http.client.methods.HttpGet;
  9. import org.apache.http.client.protocol.HttpClientContext;
  10. import org.apache.http.conn.ConnectTimeoutException;
  11. import org.apache.http.conn.HttpClientConnectionManager;
  12. import org.apache.http.impl.client.CloseableHttpClient;
  13. import org.apache.http.impl.client.HttpClients;
  14. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
  15. import org.apache.http.protocol.HttpContext;
  16. import org.apache.http.util.EntityUtils;
  17. import javax.net.ssl.SSLException;
  18. import javax.net.ssl.SSLHandshakeException;
  19. import java.io.IOException;
  20. import java.io.InterruptedIOException;
  21. import java.net.UnknownHostException;
  22. /**
  23. * 单线程-使用连接池管理HTTP请求
  24. * @author: liangpengju
  25. * @version: 1.0
  26. */
  27. public class HttpConnectManager {
  28. public static void main(String[] args) throws Exception {
  29. //创建HTTP的连接池管理对象
  30. PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
  31. //将最大连接数增加到200
  32. connectionManager.setMaxTotal(200);
  33. //将每个路由的默认最大连接数增加到20
  34. connectionManager.setDefaultMaxPerRoute(20);
  35. //将http://www.baidu.com:80的最大连接增加到50
  36. //HttpHost httpHost = new HttpHost("http://www.baidu.com",80);
  37. //connectionManager.setMaxPerRoute(new HttpRoute(httpHost),50);
  38. //发起3次GET请求
  39. String url ="https://www.baidu.com/s?word=java";
  40. long start = System.currentTimeMillis();
  41. for (int i=0;i<100;i++){
  42. doGet(connectionManager,url);
  43. }
  44. long end = System.currentTimeMillis();
  45. System.out.println("consume -> " + (end - start));
  46. //清理无效连接
  47. new IdleConnectionEvictor(connectionManager).start();
  48. }
  49. /**
  50. * 请求重试处理
  51. * @param tryTimes 重试次数
  52. * @return
  53. */
  54. public static HttpRequestRetryHandler retryHandler(final int tryTimes){
  55. HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
  56. @Override
  57. public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
  58. // 如果已经重试了n次,就放弃
  59. if (executionCount >= tryTimes) {
  60. return false;
  61. }
  62. // 如果服务器丢掉了连接,那么就重试
  63. if (exception instanceof NoHttpResponseException) {
  64. return true;
  65. }
  66. // 不要重试SSL握手异常
  67. if (exception instanceof SSLHandshakeException) {
  68. return false;
  69. }
  70. // 超时
  71. if (exception instanceof InterruptedIOException) {
  72. return false;
  73. }
  74. // 目标服务器不可达
  75. if (exception instanceof UnknownHostException) {
  76. return true;
  77. }
  78. // 连接被拒绝
  79. if (exception instanceof ConnectTimeoutException) {
  80. return false;
  81. }
  82. // SSL握手异常
  83. if (exception instanceof SSLException) {
  84. return false;
  85. }
  86. HttpClientContext clientContext = HttpClientContext .adapt(context);
  87. HttpRequest request = clientContext.getRequest();
  88. // 如果请求是幂等的,就再次尝试
  89. if (!(request instanceof HttpEntityEnclosingRequest)) {
  90. return true;
  91. }
  92. return false;
  93. }
  94. };
  95. return httpRequestRetryHandler;
  96. }
  97. /**
  98. * doGet
  99. * @param url 请求地址
  100. * @param connectionManager
  101. * @throws Exception
  102. */
  103. public static void doGet(HttpClientConnectionManager connectionManager,String url) throws Exception {
  104. //从连接池中获取client对象,多例
  105. CloseableHttpClient httpClient = HttpClients.custom()
  106. .setConnectionManager(connectionManager)
  107. .setRetryHandler(retryHandler(5)).build();
  108. // 创建http GET请求
  109. HttpGet httpGet = new HttpGet(url);
  110. // 构建请求配置信息
  111. RequestConfig config = RequestConfig.custom().setConnectTimeout(1000) // 创建连接的最长时间
  112. .setConnectionRequestTimeout(500) // 从连接池中获取到连接的最长时间
  113. .setSocketTimeout(10 * 1000) // 数据传输的最长时间10s
  114. .setStaleConnectionCheckEnabled(true) // 提交请求前测试连接是否可用
  115. .build();
  116. // 设置请求配置信息
  117. httpGet.setConfig(config);
  118. CloseableHttpResponse response = null;
  119. try {
  120. // 执行请求
  121. response = httpClient.execute(httpGet);
  122. // 判断返回状态是否为200
  123. if (response.getStatusLine().getStatusCode() == 200) {
  124. String content = EntityUtils.toString(response.getEntity(), "UTF-8");
  125. System.out.println("内容长度:" + content.length());
  126. }
  127. } finally {
  128. if (response != null) {
  129. response.close();
  130. }
  131. // 此处不能关闭httpClient,如果关闭httpClient,连接池也会销毁
  132. // httpClient.close();
  133. }
  134. }
  135. /**
  136. * 监听连接池中空闲连接,清理无效连接
  137. */
  138. public static class IdleConnectionEvictor extends Thread {
  139. private final HttpClientConnectionManager connectionManager;
  140. private volatile boolean shutdown;
  141. public IdleConnectionEvictor(HttpClientConnectionManager connectionManager) {
  142. this.connectionManager = connectionManager;
  143. }
  144. @Override
  145. public void run() {
  146. try {
  147. while (!shutdown) {
  148. synchronized (this) {
  149. //3s检查一次
  150. wait(3000);
  151. // 关闭失效的连接
  152. connectionManager.closeExpiredConnections();
  153. }
  154. }
  155. } catch (InterruptedException ex) {
  156. // 结束
  157. ex.printStackTrace();
  158. }
  159. }
  160. public void shutdown() {
  161. shutdown = true;
  162. synchronized (this) {
  163. notifyAll();
  164. }
  165. }
  166. }
  167. }

多线程-HttpClient连接池管理HTTP请求实例

主要步骤:

  1. 创建HTTP的连接池管理对象cm,如下所示
  1. PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

2.设置最大连接数和每个路由的默认最大连接数等参数,如下所示

  1. //将最大连接数增加到200
  2. cm.setMaxTotal(200);
  3. //将每个路由的默认最大连接数增加到20
  4. cm.setDefaultMaxPerRoute(20);

3.创建HttpClients对象的并设置连接池对象,HttpClients对象创建对象所示:

  1. CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();

4.继承Thread类实现一个执行Get请求线程类GetThread,重载run()方法,执行HttpGet请求;
5.定义要请求的URI地址为数组形式,为每一个URI创建一个GetThread线程,并启动所有线程;
Java实现源码:

  1. package com.liangpj.develop.httpclient;
  2. import org.apache.http.HttpEntity;
  3. import org.apache.http.client.ClientProtocolException;
  4. import org.apache.http.client.methods.CloseableHttpResponse;
  5. import org.apache.http.client.methods.HttpGet;
  6. import org.apache.http.client.protocol.HttpClientContext;
  7. import org.apache.http.impl.client.CloseableHttpClient;
  8. import org.apache.http.impl.client.HttpClients;
  9. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
  10. import org.apache.http.protocol.HttpContext;
  11. import java.io.IOException;
  12. /**
  13. * 多线程-HttpClient连接池管理HTTP请求实例
  14. */
  15. public class MultiThreadHttpConnManager {
  16. public static void main(String[] args) {
  17. //连接池对象
  18. PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
  19. //将最大连接数增加到200
  20. connectionManager.setMaxTotal(200);
  21. //将每个路由的默认最大连接数增加到20
  22. connectionManager.setDefaultMaxPerRoute(20);
  23. //HttpClient对象
  24. CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
  25. //URIs to DoGet
  26. String[] urisToGet = {
  27. "https://www.baidu.com/s?word=java",
  28. "https://www.baidu.com/s?word=java",
  29. "https://www.baidu.com/s?word=java",
  30. "https://www.baidu.com/s?word=java"
  31. };
  32. //为每一个URI创建一个线程
  33. GetThread[] threads = new GetThread[urisToGet.length];
  34. for (int i=0;i<threads.length;i++){
  35. HttpGet httpGet = new HttpGet(urisToGet[i]);
  36. threads[i] = new GetThread(httpClient,httpGet);
  37. }
  38. //启动线程
  39. for (int j=0;j<threads.length;j++){
  40. threads[j].start();
  41. }
  42. //join 线程
  43. for(int k=0;k<threads.length;k++){
  44. try {
  45. threads[k].join();
  46. } catch (InterruptedException e) {
  47. e.printStackTrace();
  48. }
  49. }
  50. }
  51. /**
  52. * 执行Get请求线程
  53. */
  54. public static class GetThread extends Thread{
  55. private final CloseableHttpClient httpClient;
  56. private final HttpContext context;
  57. private final HttpGet httpget;
  58. public GetThread(CloseableHttpClient httpClient, HttpGet httpget) {
  59. this.httpClient = httpClient;
  60. this.context = HttpClientContext.create();
  61. this.httpget = httpget;
  62. }
  63. @Override
  64. public void run() {
  65. try {
  66. CloseableHttpResponse response = httpClient.execute(httpget,context);
  67. try {
  68. HttpEntity entity = response.getEntity();
  69. }finally {
  70. response.close();
  71. }
  72. }catch (ClientProtocolException ex){
  73. //处理客户端协议异常
  74. }catch (IOException ex){
  75. //处理客户端IO异常
  76. }
  77. }
  78. }
  79. }

想要获取HttpClient实战的所有实例代码,可以关注Java技术日志订阅号后,在消息框回复关键字:httpclient可以获取代码的地址。

HttpClient实战二:单线程和多线程连接池实例的更多相关文章

  1. Java实战之04JavaWeb-05事务和连接池

    一.事务部分 1.事务的简介 做一件事情,这个一件事情中有多个组成单元,这个多个组成单元要不同时成功,要不同时失败.A账户转给B账户钱,将A账户转出钱的操作与B账户转入钱的操作绑定到一个事务中,要不这 ...

  2. HttpClient官方sample代码的深入分析(连接池)

    前言   之前一直使用apache的httpclient(4.5.x), 进行http的交互处理. 而httpclient实例则使用了http连接池, 而一旦涉及到连接池, 那会不会在使用上有些隐藏很 ...

  3. Python3 多线程(连接池)操作MySQL插入数据

    1.主要模块DBUtils : 允许在多线程应用和数据库之间连接的模块套件Threading : 提供多线程功能 2.创建连接池PooledDB 基本参数: mincached : 最少的空闲连接数, ...

  4. mybatis深入理解(二)-----Mybatis数据源与连接池

    对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题.本文将通过对MyBatis框架的数据源结构进行详尽的分析,并且深入解析MyBatis的连接池.本文首先会讲述MyBa ...

  5. Java基础学习笔记二十七 DBUtils和连接池

    DBUtils 如果只使用JDBC进行开发,我们会发现冗余代码过多,为了简化JDBC开发,本案例我们讲采用apache commons组件一个成员:DBUtils.DBUtils就是JDBC的简化开发 ...

  6. Jedis 连接池实例

    package com.java56.redis; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; im ...

  7. Python实战(6)单线程和多线程导入mysql数据对比测试

    单线程脚本 导入文件的行数 # wc -l /data/logs/testlog/20120219/testlog1/* 1510503 total # -*- coding: utf-8 -*- # ...

  8. JDBC(二)—— 获取连接池方式

    ## 获取数据库连接的方式 ### 方式一 ```javaDriver driver = new com.mysql.cj.jdbc.Driver(); String url = "jdbc ...

  9. postgres高可用学习篇二:通过pgbouncer连接池工具来管理postgres连接

    安装pgbouncer yum install libevent -y yum install libevent-devel -y wget http://www.pgbouncer.org/down ...

随机推荐

  1. MySQL 二进制文件恢复

    先不说话  先来一段代码块 mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name ...

  2. python 之 决策树分类算法

    发现帮助新手入门机器学习的一篇好文,首先感谢博主!:用Python开始机器学习(2:决策树分类算法) J. Ross Quinlan在1975提出将信息熵的概念引入决策树的构建,这就是鼎鼎大名的ID3 ...

  3. 搭建OpenStack先电云平台

    实际操作示意图 在VMware里面创建两台centos7的虚拟机作为搭建云平台的两节点配置如下: 1.第一台虚拟机   作为控制节点 2CPU 3G以上内存 硬盘50G 网络适配器一个nat 一个仅主 ...

  4. JpGraph使用详解http://5ydycm.blog.51cto.com/115934/177498 http://www.cnblogs.com/txw1958/archive/2013/08/18/php-charts.html

    下载 在官方网站 http://www.aditus.nu/jpgraph/ 下载jpgraph,其中1.X系列是用于PHP4的,2.X系列是用于PHP5的. 安装 将下载的得到的jpgraph压缩文 ...

  5. DeepFM模型理论及代码实现

    论文地址:DeepFM: A Factorization-Machine based Neural Network for CTR Prediction

  6. 长短时记忆网络(LSTM)

    长短时记忆网络 循环神经网络很难训练的原因导致它的实际应用中很处理长距离的依赖.本文将介绍改进后的循环神经网络:长短时记忆网络(Long Short Term Memory Network, LSTM ...

  7. Maven 插件管理

    偶然与巧合 舞动了蝶翼 谁的心头风起 前赴而后继 万千人追寻 荒漠唯一菩提 似擦身相遇 或擦肩而去 命运犹如险棋 无数时间线 无数可能性 终于交织向你

  8. tomcat原理解析(一):一个简单的实现

    tomcat原理解析(一):一个简单的实现 https://blog.csdn.net/qiangcai/article/details/60583330 2017年03月07日 09:54:27 逆 ...

  9. [Flutter] Android沉侵式标题栏顶部叠加层去除

    可能你的app是这样: 框起来部分和标题栏颜色并不一致. 调用下面的代码可以变成一样. import 'package:flutter/services.dart'; static SystemUiO ...

  10. 查看shell环境下,网络是否连通-curl/ping

    检查网络是否可用 curl www.baidu.com <!--STATUS OK--><html>...</html> ping www.baidu.com注意: ...