??Jedis有直連方式,所謂直連指的是Jedis每次都會新建TCP連接,使用后在斷開連接,對于平凡訪問Redis的場景顯然不是高效的使用方式,如圖:
??因此生產環境一般使用連接池的方式對Jedis連接池進行管理,如圖:
??Jedis對象預先放在池子中(JedisPool),每次要連接Redis,只需要在池子中借,用完了在歸還給池子。
??客戶端連接Redis使用的是TCP協議,直連的方式每次需要建立TCP連接,而連接池的方式是可以預先初始化好Jedis連接,所以每次只需要從Jedis連接池借用即可,而借用和歸還操作是在本地進行的,只有少量的并發同步開銷,遠遠小于新建TCP連接的開銷。另外直連的方式無法限制Jedis對象的個數,在極端情況下可能會造成連接泄漏,而連接池的形式可以有效的保護和控制資源的使用。但是直連的方式也并不是一無是處,下表給出兩種方式各自的優劣勢:
- 直連 ?優點:簡單方便,適用于少量長期連接的場景,缺點:1>存在每次新建/關閉TCP連接開銷。2>資源無法控制,極端情況會出現連接泄漏。3>Jedis對象線程不安全。
- 連接池?優點:1>無需每次連接都生成Jedis對象,降低開銷。2>使用連接池的形式保護和控制資源的使用。缺點:相對于直連,使用相對麻煩,尤其在資源的管理上需要很多參數來保證,一旦不合理也會出現問題。
??Jedis提供了JedisPool這個類作為對Jedis的連接池,同時使用了Apache的通用對象池工具common-pool作為資源的管理工具,下面是使用JedisPool操作Redis的代碼示例:
1)Jedis連接池(通常JedisPool是單例的):
//common-pool連接池配置,這里使用默認配置,后面小節會介紹具體配置說明
GenericObjectPoolConfig poolConfig=new GenericObjectPoolConfig();
//初始化Jedis連接池
JedisPool jedisPool=new JedisPool(poolConfig,"127.0.0.1",6379);
- 獲取Jedis對象不再是直接生成一個Jedis對象進行直連,而是從連接池直接獲取,代碼如下:
Jedis jedis =null;
try {
//1.從連接池獲取jedis對象
jedis=jedisPool.getResource();
//2.執行操作
jedis.get("hello");
} catch (Exception e) {
logger.error(e.getMessage(),e);
}finally{
if(jedis!=null){
//如果使用JedisPool,close操作不是關閉連接,代表歸還連接池
jedis.close();
}
}
??這里可以看到finally中依然是jedis.close()操作,為什么會把連接關閉呢,這不和連接池的原則違背嗎?但實際上Jedis的close()實現方式如下:
public void close(){
//使用Jedis連接池
if(dataSource!=null){
if(client.isBroken()){
this.dataSource.returnBrokenResource(this);
}else{
this.dataSource.returnResource(this);
}
//直連
}else{
client.close();
}
}
參數說明:
- dataSource!=null代表使用的是連接池,所以jedis.close()代表歸還連接給連接池,而且Jedis會判斷當前連接是否已經斷開。
- dataSource=null代表直連,jedis.close()代表關閉連接。
??前面GenericObjectPoolConfig使用的是默認配置,實際它提供有很多參數,例如池子中最大連接數,最大空閑連接數,最小空閑連接數,連接活性檢測,等等,例如下面代碼:
GenericObjectPoolConfig poolConfig=new GenericObjectPoolConfig();
//設置最大連接數為默認值的5倍
poolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL*5);
//設置最大空閑連接數為默認值的3倍
poolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE*3);
//設置最小空閑連接數為默認值的2倍
poolConfig.setMinIdle(GenericObjectPoolConfig. DEFAULT_MIN_IDLE*2);
//設置開啟JMX功能
poolConfig.SetJmxEnabled(true);
//設置連接池沒有連接后客戶端的最大等待時間(單位為毫秒)
poolConfig.setMaxWaitMillis(3000);
GenericObjectPoolConfig的其他屬性:
基本參數
lifo ?//GenericObjectPool 提供了后進先出(LIFO)與先進先出(FIFO)兩種行為模式的池。默認為true,即當池中有空閑可用的對象時,調用borrowObject方法會返回最近(后進)的實例
fairness ?//當從池中獲取資源或者將資源還回池中時 是否使用java.util.concurrent.locks.ReentrantLock.ReentrantLock 的公平鎖機制,默認為false
數量控制參數
maxTotal ?//鏈接池中最大連接數,默認為8
maxIdle ?//鏈接池中最大空閑的連接數,默認也為8
minIdle ?//連接池中最少空閑的連接數,默認為0
超時參數
maxWaitMillis ?//當連接池資源耗盡時,等待時間,超出則拋異常,默認為-1即永不超時
blockWhenExhausted ?//連接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true。當這個值為true的時候,maxWaitMillis參數才能生效。為false的時候,當連接池沒資源,則立馬拋異常。默認為true
test參數
testOnCreate ?//默認false,create的時候檢測是有有效,如果無效則從連接池中移除,并嘗試獲取繼續獲取
testOnBorrow ?//默認false,borrow的時候檢測是有有效,如果無效則從連接池中移除,并嘗試獲取繼續獲取
testOnReturn ?//默認false,return的時候檢測是有有效,如果無效則從連接池中移除,并嘗試獲取繼續獲取
testWhileIdle ?//默認false,在evictor線程里頭,當evictionPolicy.evict方法返回false時,而且testWhileIdle為true的時候則檢測是否有效,如果無效則移除
檢測參數
timeBetweenEvictionRunsMillis ?//空閑鏈接檢測線程檢測的周期,毫秒數。如果為負值,表示不運行檢測線程。默認為-1.
numTestsPerEvictionRun ?//在每次空閑連接回收器線程(如果有)運行時檢查的連接數量,默認為3
minEvictableIdleTimeMillis ?//連接空閑的最小時間,達到此值后空閑連接將可能會被移除。默認為1000L 60L 30L
softMinEvictableIdleTimeMillis ?//連接空閑的最小時間,達到此值后空閑鏈接將會被移除,且保留minIdle個空閑連接數。默認為-1.
evictionPolicyClassName ?//evict策略的類名,默認為org.apache.commons.pool2.impl.DefaultEvictionPolicy
其他
jmxEnabled ?//是否開啟jmx監控,如果應用開啟了jmx端口并且jmxEnabled設置為true,就可以通過jconsole或者jvisualvm看到關于連接池的相關統計,有助于了解連接池的使用情況,并且可以針對其做監控統計,默認是true。
evictionPolicyClassName ?//設置的逐出策略類名, 默認"org.apache.commons.pool2.impl.DefaultEvictionPolicy"(當連接超過最大空閑時間,或連接數超過最大空閑連接數)