HttpComponents分析之连接池实现

                                                                         

      想象一下喵喵星人和汪星人星人和汪星人在打电话时的情景:

 

      1. 喵星人先找到汪星人电话号码,

      2. 再拨汪星人电话号码,等待

      3. 喵星人接通电话后说一句“miao"

      4. 汪星人收到"miao"后进行通话"wang",

         双方都可听到对方的讲话时,电话线路就通了。

 

       这个过程是相当耗时的,特别是接通电话说了一句”@#!$%@#”就挂了.每隔一小段时间,发现还有话要说,然后反复的进行上述过程建立连接,这样的通话效率是非常低下的。打电话过程就相当于TCP的三次握手,说话交流相当于在其上传输的通信信息。但其实早期的Http就是这样的,一次http请求完成后,立即关闭连接。如果请求的数据非常少而次数又极多,那么通讯效率是非常低的。如何提高通讯的效率呢?其实很简单,只需在建立连接后,完成通话先等待一段时间,看对方在这段时间内是否还有话说,如果有话说,那么继续通信,否则过了这段时间后就关闭连接。这种解决方案在Http协议中也有体现,即keep-alive。

 

      回到主题,Http协议是互联网上最流行的协议,webservices,基于网络的应用等在增加Http协议支持的需求同时,也强有力的推动协议本身从浏览器应用的局限性场合扩张出来。虽然java.net对http的协议从网络上获取资源等功能做了基本的支持,但它并不能满足许多应用对协议全面功能和灵活性的要求。比如下面提到的http连接池就是一个非常重要的功能。

 

      Http连接池是利用了Http 1.1 KeepAlive的持久连接特性,在TCP协议里两个机器建立连接涉及三次握手,是比较消耗时间的,特别是在持续传送少量数据时,如果连接能够持续重用,就可以达到较大的吞吐量。同时也要考虑到如果可以对于一个服务器端口开通多个socket连接去传输信息,是可以达到网络带宽的一定提高的。用流行的一句话体来说,就是管理一个路由的多个持久连接的创建,分配,复用,回收问题。

 

图1:connPool的继承体系

pool

一、基本结构:

1. ConnPool:连接池接口:

  • Future<E> lease(final T route, final Object state, final FutureCallback<E> callback);

从连接池中取出连接

  • void release(E entry, boolean reusable);

释放连接

2. AbstractConnPool:

其中主要有如下数据结构:

  • Map<T, RouteSpecificPool<T, C, E>> routeToPool 每个路由对应其连接池的映射,
  • Set<E> leased总池出借的连接集合,LinkedList<E> available 总池可用的连接集合,
  • LinkedList<PoolEntryFuture<E>> pending总池等待取连接的队列,
  • Map<T, Integer> maxPerRoute 每个路由的最大连接数量映射表。
  • int maxTotal 总池的连接最大数。

并依靠上述数据结构进行了资源同步和生命周期方法如shutdown()等操作。

 

图2:routeToPool结构图

RouteToPool

 

其中routeToPool里面不同的路由有各自的RouteSpecificPool(路由相关连接池),其中也有三个数据结构:

  • Pending 同路由等待取连接的队列
  • Avaliable 同路由可以使用的连接队列。
  • Leased:同路由已经租赁使用的连接集合。

 

二、主要操作分析

 

Lease 租赁连接:

1. 先从routeToPool找到当前路由对应的连接池pool

2. 再去连接池pool找空闲的连接,并观察其是否是关闭或者超时的连接,是则将其关闭,并再查找下一个空闲连接,直到找到或者遍历完可用连接链表avaliable为止。

3. 如果找到,则在可用连接链表avaliable中移除entry,并将其加入到租赁集合Leased中去,并返回。

4. 如果找不到,那么就查询每个路由连接最大上限映射表maxPerRoute找到当前路由最大上限maxPerRoute,并检查当前路由连接数+1的方案是否超过了本路由最大上限,如果超过,则将此路由对应的连接池avaliable队列中最早使用的连接关闭。

5. 检查当前路由对应的池中已有的连接数是否超过上限maxPerRoute且已有的连接总数是否小于总池中的最大计数maxTotal;

如果满足条件,再检查avaliable队列中连接的数量是否大于等于总池可分配连接数,满足则尝试从总池可用连接链表avaliable中选取最早入队的连接,并在此连接相应的路由对应的池中进行连接关闭;

最后创建新的连接并加入总池和路由对应的leased集合中,创建成功则返回。

6. 如果步骤5种条件不满足,那么将其加入到等待队列pending中去,并进入等待模式。

7. 如有人唤醒后再检查超时,如果没有超时则跳回到2。

 

Release归还连接:

1. 先从总池中归还连接

2. 如果1成功,再从路由对应的连接归还

3. 最后通知等待唤醒取连接的pending队列中的任务继续去获取连接。

posted on 2013-03-25 10:16  jinspire  阅读(5122)  评论(2编辑  收藏  举报

导航