随着微服务的流行,服务之间的http调用越来越多。在java里面我们可以使用httpclient这个开源工具类来进行处理,但若使用不当,可能性能会比较差,尤其是连接池是否能正常使用。接下来会详细分析下httpclient的连接池原理。

1. 使用httpclient的好处

1)、降低延迟:如果不采用连接池,每次连接发起Http请求的时候都会重新建立TCP连接(经历3次握手),用完就会关闭连接(4次挥手),如果采用连接池则减少了这部分时间损耗。

2)、支持更大的并发:如果不采用连接池,每次连接都会打开一个端口,在大并发的情况下系统的端口资源很快就会被用完,导致无法建立新的连接。采用连接池来管理长连接,可以复用之前的连接,而我们使用httpclient,有可能是两个集群之间调用,也就是有限的机器之间进行调用。这样复用连接池,能有效的节约资源。

2. 长连接和短连接

因为httpclient连接池都是管理的基于长连接的socket连接。所以介绍下长连接和短连接的区别。首先需要说下http keep-alive与tcp keep-alive和区别,http keep-alive与tcp keep-alive,不是同一回事,意图不一样。http keep-alive是为了让tcp活得更久一点,以便在同一个连接上传送多个http,提高socket的效率。而tcp keep-alive是TCP的一种检测TCP连接状况的保鲜机制。tcp keep-alive保鲜定时器,支持三个系统内核配置参数:

echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time

echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl

echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes

keepalive是TCP保鲜定时器,当网络两端建立了TCP连接之后,闲置idle(双方没有任何数据流发送往来)了tcp_keepalive_time后,服务器内核就会尝试向客户端发送侦测包,来判断TCP连接状况(有可能客户端崩溃、强制关闭了应用、主机不可达等等)。如果没有收到对方的回答(ack包),则会在 tcp_keepalive_intvl后再次尝试发送侦测包,直到收到对对方的ack,如果一直没有收到对方的ack,一共会尝试 tcp_keepalive_probes次,每次的间隔时间在这里分别是15s, 30s, 45s, 60s, 75s。如果尝试tcp_keepalive_probes,依然没有收到对方的ack包,则会丢弃该TCP连接。TCP连接默认闲置时间是2小时,一般设置为30分钟足够了。

在httpclient中管理http层 keep-alive,创建连接并获得请求结果后,会对该连接设置长连接策略,如在MinimalClientExec.java类中,

httpProcessor.process(request, context);

final HttpResponse response = requestExecutor.execute(request, managedConn, context);

httpProcessor.process(response, context);

// The connection is in or can be brought to a re-usable state.

if (reuseStrategy.keepAlive(response, context)) {

// Set the idle duration of this connection

final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);

releaseTrigger.setValidFor(duration, TimeUnit.MILLISECONDS); // 如果是长连接,此处设置过期时间。

releaseTrigger.markReusable();

} else {

releaseTrigger.markNonReusable();

}

在看看默认的长连接设置策略:

public class DefaultConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {

public static final DefaultConnectionKeepAliveStrategy INSTANCE = new DefaultConnectionKeepAliveStrategy();

public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {

Args.notNull(response, "HTTP response");

final HeaderElementIterator it = new BasicHeaderElementIterator(

response.headerIterator(HTTP.CONN_KEEP_ALIVE));

while (it.hasNext()) {

final HeaderElement he = it.nextElement();

final String param = he.getName();

final String value = he.getValue();

if (value != null && param.equalsIgnoreCase("timeout")) {

try {

return Long.parseLong(value) * 1000; // 默认返回的是1S

} catch(final NumberFormatException ignore) {

}

}

}

return -1;

}

}

当然也可以自己实现ConnectionKeepAliveStrategy类,来实现自己的长连接策略。

ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {

public long getKeepAliveDuration(HttpResponse response, HttpContext context) {

// Honor 'keep-alive' header

HeaderElementIterator it = new BasicHeaderElementIterator(

response.headerIterator(HTTP.CONN_KEEP_ALIVE));

while (it.hasNext()) {

HeaderElement he = it.nextElement();

String param = he.getName();

String value = he.getValue();

if (value != null && param.equalsIgnoreCase("timeout")) {

try {

return Long.parseLong(value) * 1000;

} catch(NumberFormatException ignore) {

}

}

}

HttpHost target = (HttpHost) context.getAttribute(

HttpClientContext.HTTP_TARGET_HOST);

// 对特定的域名进行长连接设置

if ("www.baidu.com".equalsIgnoreCase(target.getHostName())) {

// Keep alive for 5 seconds only

return 5 * 1000;

} else {

// otherwise keep alive for 30 seconds

return 30 * 1000;

}

}

};

CloseableHttpClient client = HttpClients.custom()

.setKeepAliveStrategy(myStrategy)// 设置为自己定义的长连接策略

.build();

3. Httpclient连接池原理

连接池的结构如下:

其中CPoolEntry是连接实体,leased代表被占用的连接集合,avaliabled代表可用的连接的集合,pending代表阻塞状态的连接。外层的leased,avaliabled,pending是全局的,用来限制全局的数量,里面有各个小的routeToPool,对应不同的server机器。

从连接池获取可用连接的过程为:

1). 每个server对应一个routeToPool,从对应的routeToPool中获取可用的连接,有则返回该连接。若没有则转入下一步。

2). 若routeToPool和外层CPool连接池均还有可用的空间,则新建连接,并将该连接作为可用连接返回;否则进行下一步

3). 将当前请求放入pending队列,等待执行。

4). 上述过程中会判断各种条件是否满足,比如不能超过我们设置的总连接数等。

CPool继承的基类:AbstractConnPool,其代码结构如下

public abstract class AbstractConnPool>

implements ConnPool, ConnPoolControl {

private final Lock lock;

private final ConnFactory connFactory;

private final Map> routeToPool; //路由和连接之间的对应关系

private final Set leased; // 被占用的连接

private final LinkedList available; // 可用连接

private final LinkedList> pending;

private final Map maxPerRoute;

private volatile boolean isShutDown;

private volatile int defaultMaxPerRoute;

private volatile int maxTotal;

private volatile int validateAfterInactivity;

从上述代码可以看出CPoolEntry便是线程池里面的一个个元素,CPool里面包含routeToPool这个小的线程池,routeToPool里面都是相对于一个固定的HttpRoute(也可以说到一个固定的机器地址)所建立的所有连接。

4. HttpClient参数配置

HttpClient有多个参数可以配置,比如:

PoolingClientConnectionManager conMgr = new PoolingClientConnectionManager();

conMgr.setMaxTotal(200); //设置整个连接池最大连接数,每个routeToPool的大小加起来不能超过maxTotal的值。

conMgr.setDefaultMaxPerRoute(20);//这个便是设置的routeToPool线程池的大小。

还可以设置请求超时时间,socket等待数据超时时间,从连接池获取不到数据时的等待时间。

通过以上分析可知,通过合理的设置连接池的长连接策略,连接池的大小,以及一些连接相关的参数,能有效的提高系统的并发量。HttpClient的其它一些特性诸如自动管理cookie,支持HTTPS协议,以可扩展的面向对象的结构实现了http的全部方法等,增加了易用性和灵活性,也方便了我们的使用。

http 连接池 java_HttpClient 连接池管理相关推荐

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

    以下文章来源方志朋的博客,回复"666"获面试宝典 一.背景 HTTP协议是无状态的协议,即每一次请求都是互相独立的.因此它的最初实现是,每一个http请求都会打开一个tcp so ...

  2. 什么是线程池,连接池,线程池和连接池之间的区别

    线程池概念: 线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用 new 线程而是直接去池中拿线程即可,节 省了开辟子线程的时间,提高的代码执行效率. 线程池的优点: 第一:降低资源消耗. ...

  3. mysql持久连接_持久性连接,短连接和连接池

    1.长连接在没有数据通信时,定时发送数据包,以维持连接状态,短连接在没有数据传输时直接关闭就行了 2.什么时候用长连接,短连接? 长连接主要用于在少数客户端与服务端的频繁通信,因为这时候如果用短连接频 ...

  4. 数据库连接池-连接的关闭内幕

    们经常会遇到这样那样的连接未关闭的问题,连接没有及时关闭导致的直接后果就是内存泄漏直至down机.我们也都知道解决的方式,但是在解决了问题之后经常会思考为什么会这样呢?连接close()掉,然后在创建 ...

  5. 连接池,数据库连接池

    连接池 连接池是什么? 连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用. 连接池的优点 减少连接创建时间 简化的编程模式 受控的资源使用 注:连接池能够使性能最大化, ...

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

    转载自  Http 持久连接与 HttpClient 连接池 一.背景 HTTP协议是无状态的协议,即每一次请求都是互相独立的.因此它的最初实现是,每一个http请求都会打开一个tcp socket连 ...

  7. java构建内存池队列_池化技术(线程池、连接池、内存池等)

    一.池化技术 - 简单点来说,就是提前保存大量的资源,以备不时之需. 对于线程,内存,oracle的连接对象等等,这些都是资源,程序中当你创建一个线程或者在堆上申请一块内存时,都涉及到很多系统调用,也 ...

  8. c3p0 服务启动获取连接超时_JDBC数据库连接池

    连接池的本质是构建一个容器,容器是用来存创建好的线程,http连接.数据库连接.netty连接等 各个连接池的使用大致分为三个部分 1.首先是初始化连接池,根据设置相应的参数.连接池的大小.核心连接数 ...

  9. HttpClient连接池的连接保持、超时和失效机制

    HTTP是一种无连接的事务协议,底层使用的还是TCP,连接池复用的就是TCP连接,目的就是在一个TCP连接上进行多次的HTTP请求从而提高性能.每次HTTP请求结束的时候,HttpClient会判断连 ...

  10. 连接池以及连接池的使用

    目录 什么是连接池? 为什么要使用连接池 连接池分类 连接池的核心对象 连接池对象 连接池工具类,深入理解: 连接池工具类全部代码: 什么是连接池? 一种存放数据库连接的容器,并且拥有动态新增连接.管 ...

最新文章

  1. 线程安全(中)--彻底搞懂synchronized(从偏向锁到重量级锁)
  2. 请问如何在出错后能恢复到改变设置前的状态?
  3. this调用语句必须是构造函数中的第一个可执行语句_谈谈JavaScript中的函数构造式和new关键字...
  4. JavaScript实现使用 BACKTRACKING 方法查找集合的幂集算法
  5. ArrayList用法说明
  6. JSF AJAX请求的会话超时处理
  7. 【 Grey Hack 】万金油脚本:常见端口获取shell
  8. [js] 在设置keyup监听事件后按F5刷新和按浏览器中刷新键刷新有什么区别?
  9. 对称式加密和非对称式加密
  10. Java 字符串格式化 String.format()
  11. 做windows界面,用QT还是MFC?
  12. 创建LEANGOO账号
  13. redis在应用中使用连接不释放问题解决
  14. 四六级英语都考过,让你见识一下“八级程序员”
  15. 第一章 概率与统计介绍--机器学习数学基础
  16. PHPCAS客户端搭建教程
  17. CAD .NET 14.1.X DWG/ DXF, PLT 自由转换- CNC
  18. processing作业:画有五角星绕圈的国旗(此例:塔吉克斯坦共和国国旗)
  19. 网站推广----献给广大站长(转载)
  20. 什么是VI?(虚拟仪器)

热门文章

  1. android studio systrace,Android Systrace 基础知识 -- Systrace 简介
  2. 皇家彩世界教程iOS性能优化系列篇之“优化总体原则”
  3. Java获取字符串长度(length())
  4. 如何解决没有指定在Windows运行或者它包含错误
  5. 晨曦记账本记账收支,统计结余
  6. 游戏测试用例及游戏测试bug详解
  7. linux终端打开excel,linux 打开excel命令 java
  8. 服务器无线网卡ap软件,无线网卡虚拟成无线AP工具(将无线网卡虚拟成无线AP软件) 2017最新版...
  9. android screenshot流程,APP中,Screenshot的设计要领和各发布渠道的要求
  10. 大数据产品推荐:金蜂巢大数据集成与脱敏系统