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隊列中的任務繼續去獲取連接。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM