博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HttpClient实战二:单线程和多线程连接池实例
阅读量:5304 次
发布时间:2019-06-14

本文共 11635 字,大约阅读时间需要 38 分钟。

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

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

主要步骤:

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

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

1  //将最大连接数增加到2002  cm.setMaxTotal(200);3 //将每个路由的默认最大连接数增加到204  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 /**14  * 多线程-HttpClient连接池管理HTTP请求实例15  */16 public class MultiThreadHttpConnManager {17     public static void main(String[] args) {18         //连接池对象19         PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();20       //将最大连接数增加到20021         connectionManager.setMaxTotal(200);22         //将每个路由的默认最大连接数增加到2023         connectionManager.setDefaultMaxPerRoute(20);24         //HttpClient对象25         CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();26         //URIs to DoGet27         String[] urisToGet = {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                 "https://www.baidu.com/s?word=java"32         };33         //为每一个URI创建一个线程34         GetThread[] threads = new GetThread[urisToGet.length];35         for (int i=0;i

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

转自:

转载于:https://www.cnblogs.com/fnlingnzb-learner/p/10833004.html

你可能感兴趣的文章
pip install torch on windows, and the 'from torch._C import * ImportError: DLL load failed:' s...
查看>>
环套树
查看>>
java基础(一):我对java的三个环境变量的简单理解和配置
查看>>
arcgis api 4.x for js 结合 Echarts4 实现散点图效果(附源码下载)
查看>>
YTU 2625: B 构造函数和析构函数
查看>>
apache自带压力测试工具ab的使用及解析
查看>>
C#使用Xamarin开发可移植移动应用(2.Xamarin.Forms布局,本篇很长,注意)附源码
查看>>
jenkins搭建
查看>>
C#中使用Split分隔字符串的技巧
查看>>
eclipse的调试方法的简单介绍
查看>>
加固linux
查看>>
IPSP问题
查看>>
(转)Java中的String为什么是不可变的? -- String源码分析
查看>>
HNU 10362 A+B for Input-Output Practice (II)
查看>>
十. 图形界面(GUI)设计9.列表和组合框
查看>>
10.17动手动脑
查看>>
js index of()用法
查看>>
WPF中Image显示本地图片
查看>>
哈希表等概率情况下查找成功和查找不成功的平均查找长度的计算
查看>>
Windows Phone 7你不知道的8件事
查看>>