最近工做中遇到一個因爲對commons-pool的使用不當而引起的問題,習得正確的使用姿式後,寫下這個簡單的故事,幫助理解Apache Commons Pool的工做原理。java
Apache Commons Pool, Java界無人不知無人不曉的對象池技術, 經常使用於實現各類鏈接池, 如數據庫鏈接池, Redis鏈接池等git
下面以租車公司爲例子說明這張圖,介紹commons pool的基本工做方式:github
GenericObjectPool(租車公司)redis
做爲租車公司,須要提供租車和收回歸還的車輛的兩個服務,同時它還要管理着全部的那些車輛,隨着業務發展壯大,須要買新車;對於已經不能安全駕駛的車輛,須要將其銷燬;同時還要按期對車輛進行安全檢測等。數據庫
PooledObject(租車公司的全部車輛)數組
租車公司的車輛分爲三類:空閒可租用的車輛(Idle Objects),已借出的車輛(Active Objects),認爲已丟棄的車輛(Anandoned Objects)安全
Borrow Object(租車)post
- A1: 世界那麼大,一位年輕人想租輛車出去逛逛
- A2: 老闆先看看有沒有空閒的車
- A3.1: 若是有,則將最近歸還的車借出去,並標記爲已借出(Active),若是沒有空閒的車了,就買輛,同時也標記爲已借出(這是一家不差錢的公司)
- A3.2: 老闆把標記好的車租給年輕人
Return Object(還車)ui
- B1: 世界那麼大,年輕人終於逛完了,回來還車
- B2: 老闆把車放回停車場,並把標記改成空閒狀態(Idle),能夠再被其餘人租用。
TestOnBorrow/TestOnReturn(租出/歸還時進行檢查)
這 家公司不只不差錢,它對車輛的安全還很負責,對於租出去的車,無論是從空閒車輛裏取出的,仍是新買回的,都會先檢查一遍這車的好壞,總不能坑了年輕人,如 果發現有問題,立馬再換一輛。歸還的時候,也會檢查一遍,若是有問題,就扔掉(真土豪),除此以外,公司還專門請了一位車輛安檢員,按期對閒置了一段時間 的車輛進行安全檢測(Evict Thread),一有問題也扔掉。
有借有還,看上去一切都很美好。
然而現實裏總有意外發生:
年輕人借走車後,發現世界越逛越大,久久不肯回家。安檢員按期檢查時發現這車子都借出去大半年了,還沒還回來,是否是丟了?因而掏出手機,」啪「的按了一下,遠程將車子熄了火,標記爲報廢車輛(Abandoned),看成報廢處理了。
Evict Thread(按期檢查的安檢人員)
- C1: 對於已歸還標記爲空閒的車輛,安檢員按期對它們抽查,若是超過一段時間沒有使用,看看是否壞掉,壞了就及時做廢掉(C2).
- D1: 對於標記爲已借出的對象,安檢員按期檢查時發現借出好久都未還,直接做廢(D2)。
好了,故事講完了,但願你們對Commons Pool都理解了。
有興趣的同窗能夠繼續往下看看咱們遇到的那個問題:
咱們使用Jedis做爲redis客戶端操做,在壓測環境下,時不時發現Jedis報了這個異常:ClassCastException - [B cannot be cast to java.lang.Long
網上各類google百度,發現大部分網友們說是因爲pipeline操做,出現異常時鏈接沒有正確destory掉,而直接放回鏈接池裏,被下個線程拿到後,取到鏈接中殘留的pipeline的操做結果,從而致使類型轉換錯誤。
這個解釋聽起來很在理,但反覆檢查代碼,發現對於異常的封裝都作好了,並且出現問題時也沒有使用pipeline操做,應該不是網友們說的狀況。
因而懷疑是否是鏈接池出了問題,多個線程對同一個鏈接作了不一樣的操做,獲取錯了數據致使,但大名鼎鼎的commons-pool出現這樣低級的錯誤,不可能呀?
翻了幾遍commons-pool的代碼後,發現多是上面說的那個按期檢查的安檢員搗的鬼?
對 於借出的對象,咱們配置成借出後超過10秒不歸還則做廢,理論上對於redis的操做,10秒確實也足夠了,可是咱們對JedisPool作了進一步的封 裝,在一些特殊狀況下,確實會出現持有鏈接超過10秒的狀況(這個就不展開了),致使鏈接還在被程序使用,讀取redis的數據處理時,被清理線程無辜的 銷燬了(調用jedis.quit()
),
jedis的quit命令返回值就是一個Byte數組,而咱們的操做返回是Long,因而就出現了ClassCastException - [B cannot be cast to java.lang.Long
這樣的異常。
最後的解決辦法就是將做廢時間的定義適當加大。