最近在鏈接池上面栽了個跟頭(參見這裏),引發我對池技術的強烈關注,這幾天總結了一下不少場景都會使用的池技術;html
池概念java
pool,中文翻譯爲水池,可是在英文中,還有一種解釋是redis
an organization of people or resources that can be shared;sql
不知道古代中文是否包含共享資源的意思,歐美語言中,池就是有資源共享的意思;數據庫
爲何要採用池技術apache
精確的說,應該是爲何要使用鏈接池技術;咱們先看看這些使用池技術的組件都有哪些,httpclient HTTP協議組件,dbcp數據庫鏈接池,jedis redis客戶端;能夠說表明了三種大相徑庭的應用場景;可是他們背後,卻都有一個共同點,那就是TCP長鏈接;後端
綜上,我我的認爲主要是出於如下幾方面服務器
一、TCP鏈接每次創建和釋放都比較耗時,特別是對於小的HTTP請求,若是能在業務調用時省去這段時間,則業務代碼性能更好,這就須要提早創建TCP鏈接或者過後釋放TCP鏈接;網絡
二、業務代碼會存在屢次資源調用,可是不但願TCP鏈接對象在屢次調用之間傳來傳去,這樣會讓代碼變的複雜;運維
三、組件但願提供更友好的接口,而將底層的TCP技術使用池進行了封裝;
有些人可能對長短鏈接概念不是很清楚,你們能夠簡單的認爲,像HTTP協議請求完就會與服務器的鏈接斷掉是短鏈接,一般咱們上網都是短鏈接。像開發過程當中使用的數據庫客戶端,通常會長時間與數據庫維持一個TCP鏈接,這個能夠就認爲是長鏈接。除了DB,還有redis,java中的RMI等協議都是長鏈接;
長鏈接比短鏈接各有優劣:
好處:省去每次TCP3次握手和4次揮手的過程,發送請求和響應耗時更短;
壞處:服務器切換影響比較大,一般只能經過強制手段讓客戶端從新創建鏈接才能完成後端服務的切換;單純從運維角度看,長鏈接很是不提倡;
池抽象
若是畫一張圖,我想應該是這樣
就是在一個大池子裏面,有好多資源。這些資源隨時可能被拿出去佔用或者隨時有新的資源被歸還,好借好還,再借不難;正常狀況應該是這樣
這就是池技術的基本原理,這個模型很重要,httpclient,dbcp,jedis,c3p0,druid,okhttp這些組件都使用到了池技術,你們能夠自行去官網查看;下面我再抽幾個重點場景給你們幾個常見的重要配置參數;
鏈接池總資源數
既然是池,其容量老是有限制的,而且不一樣的組件,其總量限制默認都很低。
組件 |
最大資源數屬性 |
默認最大資源數 |
httpclient4 |
MaxTotal |
20 |
jedis2 |
maxTotal |
8 |
druid |
maxActive |
8 |
c3p0 |
maxPoolSize |
15 |
關於httpclient,還要特殊說明一下,這個maxTotal,存在誤區,能夠參見這裏;
那麼問題來了,若是TCP鏈接的另一端響應忽然變慢,致使租戶沒法及時歸還資源,新的用戶又要借用,但鏈接池中沒有資源了,組件會如何處理?
答案是等;並且,若是你沒有修改默認設置的話,默認是無限的等;你可能會說,我不相等,我想讓系統有自我保護功能,當後端依賴出現問題的時候,咱們儘快的反饋給調用方,而不是把本身耗死;OK,你的想法很不錯,可是你須要修改配置,讓調用方不是無限等,能夠設置調用方不等或者等待有限時間
組件 |
屬性 |
httpclient4 |
RequestConfig.ConnectionRequestTimeout |
jedis2 |
MaxWaitMillis |
druid |
maxWait |
c3p0 |
breakAfterAcquireFailure |
dbcp |
maxWaitMillis |
TCP鏈接的問題
由於網絡協議太複雜了,當組件採用池技術後,一系列後遺症也逐漸暴露出來;有時發現從池中取出鏈接使用時,發現鏈接已經被服務器端關閉了;而且這種狀況,各類池組件沒法感知(這個提及來又能說一篇),這些鏈接在英文中稱爲stale;針對這種狀況,各類組件基本上圍繞使用流程在使用前,使用後以及定時任務清理三種策略來避免這種狀況;
使用前
組件一般採用在用戶代碼請求時,組件先本身測試TCP鏈接是否還可用,可是這種手段一般僅對DB鏈接池組件有效;如dbcp組件,一般會向服務器端發送一個測試sql來測試鏈接是否還可用;
使用後
同使用前檢測同樣,這種方法也是一般應用在數據庫鏈接池中;在數據庫出現問題時,一般鏈接已經不可用,這個時候再return給鏈接池,也會給其餘後申請者形成影響,不如直接釋放鏈接,後續再創建新的鏈接;
按期檢測
一般鏈接池在不一樣的時間,池中空閒的鏈接數量是不一樣的,在業務低峯期,長時間維持一些沒用的鏈接也是一種浪費。一般這個時候會有一個定時任務來按期清理長期不活躍的鏈接。具體的清理策略各式各樣,有按照鏈接時長清理的,有按照長時間沒有活動清理的。這個清理又會涉及不少參數設置,你們能夠自行閱讀參考;
另外,在數據庫鏈接池中針對低峯期空閒鏈接多的問題(一般會致使系統time_wait多的問題),鏈接池一般還有一個最大空閒鏈接數(maxIdle)和最小空閒鏈接數(minIdle)兩個參數,這兩個參數的含義以下:
minIdle:保證池中最少要有minIdle個空閒的鏈接可用。若是少於這個數,則開始預建立鏈接;
maxIdle:保證池中最多有maxIdle個空閒的鏈接,當鏈接池被不斷歸還時,若是空閒鏈接數超過maxIdle,則開始對空閒的鏈接數進行釋放。
最後還有一點,druid,dbcp和jedis池技術都是採用或者參考的apache的common-pool,不少參數都跟common-pool同樣。okhttp是後起之秀,雖然也使用了池技術,可是在同步調用中,並無對池的大小設置閒置,能夠認爲,okhttp是一個無限制的鏈接池;
搜索公衆號「猿界汪汪隊」,關注更多有深度的文章;
參考資料:
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/index.html
http://commons.apache.org/proper/commons-dbcp/configuration.html
https://www.mchange.com/projects/c3p0/#configuration_properties