最近在連接池上面栽了個跟頭(參見這里),引起我對池技術的強烈關注,這幾天總結了一下很多場景都會使用的池技術;
池概念
pool,中文翻譯為水池,但是在英文中,還有一種解釋是
an organization of people or resources that can be shared;
不知道古代中文是否包含共享資源的意思,歐美語言中,池就是有資源共享的意思;
為什么要采用池技術
精確的說,應該是為什么要使用連接池技術;我們先看看這些使用池技術的組件都有哪些,httpclient HTTP協議組件,dbcp數據庫連接池,jedis redis客戶端;可以說代表了三種截然不同的應用場景;但是他們背后,卻都有一個共同點,那就是TCP長連接;
綜上,我個人認為主要是出於以下幾方面
1、TCP連接每次建立和釋放都比較耗時,特別是對於小的HTTP請求,如果能在業務調用時省去這段時間,則業務代碼性能更好,這就需要提前建立TCP連接或者事后釋放TCP連接;
2、業務代碼會存在多次資源調用,但是不希望TCP連接對象在多次調用之間傳來傳去,這樣會讓代碼變的復雜;
3、組件希望提供更友好的接口,而將底層的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