Jedis連線池究竟是何物|得物技術
來源:得物技術
目錄
一、前言
二、原理概述
1. 圖示
2. 類結構
3. Jedis裡如何使用的
三、深入分析
1. 引數說明
2. 核心方法
2.1 BorrowObject
2.2 ReturnObject
四、內部機制
1. Evict(定期驅逐/保活機制)
2. Test(檢查機制)
3. Abandoned(拋棄機制)
五、排障方式
六、總結
一
前言
連線池的用途實際上有過開發經驗的朋友都已經比較清楚了,當資源物件的建立/銷燬比較耗時的場景下,可以透過"池化"技術,達到資源的複用,以此來減少系統的開銷、增大系統吞吐量,比如資料庫連線池、執行緒池、Redis 連線池等都是使用的該方式,而我們在開發場景中使用較為廣泛的 Jedis 就是使用了 GenericObjectPool 作為它底層的連線池實現。
二
原理概述
圖示
BorrowObject
業務模組透過 BorrowObject 方法從空閒連線佇列中獲取空閒連線,最長會等待 maxWaitMillis 毫秒,如果拿不到則走 Create。
ReturnObject
把連線重新放回到 IdleObjects 佇列中。
類結構
Jedis裡如何使用的
一般情況下我們在 Spring Boot 應用中會透過 Spring-Data-Redis 來使用 Redis,而在業務層會透過 RedisTemplate 來進行 Redis 的操作,但是 RedisTemplate 是怎麼來的呢?可以看到當我們引入 Spring-Data-Redis 時,就會引入 RedisAutoConfiguration,這個 AutoConfiguration 定義了,當我們存在 Jedis 的配置時且不存在 RedisTempalte 的 Bean 例項時會自動建立 Bean,核心程式碼如下圖。
而 RedisConnectionFactory 的其中一個實現就是 JedisConnectionFactory,其中就包含了 Pool。
而 Pool 本身內部就能看到我們真正的主角。
捋一下其中的關係,我們常用的 Spring-Data-Redis 的 Jedis 實現最終是透過以下的層級結構來使用 GenericObjectPool 的。
三
深入分析
引數說明
如上述類結構所示,GenericObjectPool 都是在 GenericObjectPoolConfig 或 BaseObjectPoolConfig 中進行配置相關引數的,其中核心引數以及預設值如下:
上圖對這些引數按顏色進行了一個歸類:
這裡需要注意的是,雖然 GenericObjectPool 支援我們配的引數較多,但是 Spring-Data-Redis 將這部分引數收斂了,具體可供我們修改的只有表格上面的這部分內容,其他引數,有一部分在 JedisPoolConfig 類中,繼承了 GenericObjectPoolConfig 進行了修改,比如 Spring-Data-Redis 就修改了以下引數的預設值。
testWhileIdle=true
minEvictableIdleTimeMillis=60000
timeBetweenEvictionRunsMillis=30000
numTestsPerEvictionRun=-1
核心方法
本文只會針對方法的一些核心鏈路進行說明,如想知道更多細節,針對原始碼解析的可以在網上搜尋其他相關文章或是到我的參考連結裡進行翻看。
BorrowObject
超時時間怎麼用的?
該方法用於從連線池中獲取一個空閒物件,它有可能是從空閒池中直接獲取的,或是直接建立出來的,如果第一次從空閒物件中沒有獲取到,會走建立後重新獲取,此時如果物件池目前配置的 BlockWhenExhausted=true,那麼就會受 maxWaitMillis 引數所配置的超時時間所控制,如果超過了超時時間,都沒拿到一個空閒的物件,則會直接丟擲異常。
testOnBorrow 和 testOnCreate 的使用場景
當獲取到一個物件後,由於物件池中往往存放的是諸如資料庫連線、Redis 連線等建立時較為耗時的資源,但是因為連線本身是複用的,如果 MySQL/Redis Server 端如果因為某些原因斷開/釋放了該連結,那麼此時拿到的物件就是個無效的物件,因此在 borrowObject 階段會判定,如果:
testOnBorrow=true || (create && testOnCreate=true)
就會走到:
factory.validateObject
這裡如何進行 validateObject 的,是由上層使用物件池的場景所決定的,比如在 Jedis 場景中,會向 Redis Server 傳送一個 Ping 命令,如果 Server 返回了 Pong,則認為該連線仍然有效,可以給業務層使用。
但是!!!!!!
線上環境千萬不要配置 testOnBorrow=true 或是 testOnCreate=true。
每個物件的獲取都需要先校驗再拿,會大大增加單次請求的 RT。
ReturnObject
testOnReturn 的使用場景
實際上 testOnReturn 的使用場景與上述 borrowObject 時的 testOnBorrow 是類似的,只是testOnReturn就是一個歸還物件的操作。同理,線上千萬不要配置 testOnReturn=true。
什麼時候歸還,什麼時候銷燬?
物件池中維護了一個結構為 LinkedBlockingDeque,名為 IdleObjects 的物件用於維護空閒物件佇列,且是否歸或銷燬的判斷邏輯如下:
final int maxIdleSave = getMaxIdle();if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) { ...銷燬物件}else{ ...返還至idleObjects}
如果:
物件池已經關閉(只要是程式在執行,且正常使用,不會關閉)
或
配置了 maxIdle 且空閒物件列表數量 >=maxIdle
則物件會被銷燬,否則物件會重新回到 IdleObjects 中。
四
內部機制
Evict(定期驅逐/保活機制)
週期怎麼定?
當 timeBetweenEvictionRunsMillis 配置 >0 時,在 GenericObjectPool 所繼承的基類中,會啟一個週期性執行的執行緒,它的執行週期就是 timeBetweenEvictionRunsMillis 的值。
為什麼要驅逐?
當空閒物件過多,對於客戶端或服務端的 TCP 連線維護來講,本身就是一個開銷,因此,需要有一個規則,當有一些物件實在太空閒了,就把它們踢掉。
哪些物件應該被驅逐?
首先會從空閒物件列表中挑選出一部分物件,而這個挑選過程本身也有一個規則,它受 numTestsPerEvictionRun 引數控制。
當 numTestsPerEvictionRun>0,會挑選出 numTestsPerEvictionRun 數量的空閒連線進行檢查。
當 numTestsPerEvictionRun<0 時,首先會對 numTestsPerEvictionRun 取絕對值,再然後挑選出空閒數量 /numTestsPerEvictionRun 絕對值的數量進行檢查,舉個例子,如果 numTestsPerEvictionRun=-2,就會挑選出一半進行檢查。
驅逐檢查怎麼做?
本身驅逐檢查的實現方式是支援自定義的,也就是 evictionPolicy 引數,但是往往只會選擇用預設的實現,也就是 DefaultEvictionPolicy,它的驅逐檢查策略如下:
if ((config.getIdleSoftEvictTime() < underTest.getIdleTimeMillis() && config.getMinIdle() < idleCount) || config.getIdleEvictTime() < underTest.getIdleTimeMillis()) { return true;}return false;
underTest 為被檢查物件,當存在以下場景時,滿足驅逐檢查規則,會觸發驅逐。
驅逐與保活的關係是怎麼樣的?
由於前面提到過,不能配置 testOnBorrow 和 testOnReturn,那麼如果 Server 端的連結直接斷開了,怎麼能保證池中物件的有效性呢?如果讓呼叫端呼叫時再觸發,會不會太晚了呢?這時候就有個引數 testWhileIdle,當此引數開啟時,就代表會在物件空閒時進行物件可用性檢查,具體程式碼如下:
if (evict) { destroy(underTest); destroyedByEvictorCount.incrementAndGet();} else { if (testWhileIdle) { try { factory.activateObject(underTest); } catch (final Exception e) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } }}
這裡隱掉了一些相關的非核心邏輯,這裡可以看到 testWhileIdle 的保活機制實際上和 evict 是配套使用的,如果被檢查物件需要被驅逐,也就是 evict=true,則會直接 destory 物件,否則它會判定 testWhileIdle 的狀態,此時如果 testWhileIdle=true,那麼就會啟用一下物件,具體啟用的方式是由使用物件池的上層工廠所決定的。
Test(檢查機制)
本身 GenericObjectPool 為了保證在池子中的物件有效性,會允許上層分別在幾個節點進行物件的有效性檢查,分別是:
testOnBorrow、testOnReturn、testOnCreate。
這幾個基本看名字就知道是什麼意思了,在前面講 borrowObject 和 returnObject 的時候也有提到,還有一個相對比較特別的是:
testWhileIdle。
該引數目的是為了物件在空閒期間可以進行檢查,而它的觸發實際上是和 evict(定期驅逐機制)聯合起來進行使用的。
Abandoned(拋棄機制)
實際上在提到配置引數、BorrowObject 時,還有一個機制,稱之為 Abandoned,由於本文的契機是因為 Jedis 的問題分析所寫,而 Jedis 連線池並不支援配置 Abandoned,所以本文暫不做解析,或者感興趣的可以自己到上面講的原始碼路徑去看一下,本身這個機制的理解也不是特別複雜。
五
排障方式
本身 GenericObjectPool 預設會把自己的一些引數透過 JMX 的方式進行註冊,那麼我們可以透過 Jvisualvm 進行檢視,或是透過 Arthas,輸入如下命令:
mbean org.apache.commons.pool2:type=GenericObjectPool,name=pool-redisConnectionFactory
可以獲取到物件池當前的一些屬性,如下圖:
其中對於最佳化比較有用的就是 CreatedCount(建立物件的數量)、DestoryedCount(物件銷燬的物件)、DestoryedByEvictorCount(因為驅逐機制而被銷燬的物件數量)。
六
總結
上述文章以 Jedis 為引,分析了 GenericObjectPool 連線池的底層原理以及 Jedis 是如何使用該連線池的,並且結合了 Arthas 分享了一個簡單的排障方式,實際上如果知道了 GenericObjectPool 連線池的原理,其他連線池也是大同小異,本文希望拋磚引玉,帶大家對於連線池的底層實現有個基本概念,相信以後遇到此類問題也會有分析的思路,不再迷茫~
來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70027824/viewspace-3001513/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Jedis使用連線池操作redis叢集Redis
- 解密httpclient,dbcp,jedis,c3p0,druid,okhttp都在使用的連線池技術解密HTTPclientUI
- Java 客戶端 Jedis和JedisPool 連線池Java客戶端
- jsoup爬蟲技術+druid連線池JS爬蟲UI
- 聊聊jedis連線池對commons-pool的封裝封裝
- 得物直播低延遲探索 | 得物技術
- 物聯網6類技術無線連線技術的分析
- 資料庫連線池技術詳解資料庫
- Java GenericObjectPool 物件池化技術--SpringBoot sftp 連線池工具類JavaObject物件Spring BootFTP
- 深入理解Sora技術原理|得物技術Sora
- 走進JavaWeb技術世界3:JDBC的進化與連線池技術JavaWebJDBC
- DartVM GC 深度剖析|得物技術DartGC
- Java技術分享:什麼是資料庫連線池?Java資料庫
- 知識抽取簡述|得物技術
- 下單穩定性治理 | 得物技術
- SpEL應用實戰|得物技術
- 得物技術登入元件重構元件
- 連線池
- HTTP長連線、短連線究竟是什麼?HTTP
- 得物技術多興趣召回模型實踐模型
- 【得物技術】深入理解synchronzied底層原理
- redis 原始碼分析:Jedis 哨兵模式連線原理Redis原始碼模式
- django連線池Django
- HTTP連線池HTTP
- 得物Tech Leader對管理授權的思考是什麼?|得物技術管理集錦
- 深入淺出解析JVM中的Safepoint | 得物技術JVM
- 如何從1到99做好產品 | 得物技術
- 存貨庫存模型升級始末|得物技術模型
- Http持久連線與HttpClient連線池HTTPclient
- 連線池和連線數詳解
- 2-2. 線性池技術優化優化
- ElasticSearch連線池建立Elasticsearch
- 自定義連線池
- 商家下載中心設計演進之路|得物技術
- 虛擬執行緒原理及效能分析|得物技術執行緒
- 彩虹橋架構演進之路-效能篇|得物技術架構
- 得物技術時間切片的實踐與應用
- Jedis 連線 Redis報JedisConnectionException: java.net.ConnectException: Connection refusedRedisExceptionJava