最近在連線池上面栽了個跟頭(參見這裡),引起我對池技術的強烈關注,這幾天總結了一下很多場景都會使用的池技術;
池概念
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