轉(zhuǎn)載自:http://blog.csdn.net/fachang/article/details/7984123
異常代碼如下:
- Could not get a resource from the pool
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:22)
分析:
redis.clients.util.Pool.getResource會(huì)從JedisPool實(shí)例池中返回一個(gè)可用的redis連接终畅。分析源碼可知JedisPool extends redis.clients.util.Pool<Jedis> .而Pool<T>是通過(guò)
commons-pool開(kāi)源工具包中的org.apache.commons.pool.impl.GenericObjectPool來(lái)實(shí)現(xiàn)對(duì)Jedis實(shí)例的管理的惰赋。所以我們分析一下GenericObjectPool或許能找到答案熊咽。
首先看一下common-pool的api:http://commons.apache.org/pool/apidocs/index.html?org/apache/commons/pool/impl/GenericObjectPool.html噩斟。
其中三個(gè)重要個(gè)幾個(gè)屬性是:
MaxActive: 可用連接實(shí)例的最大數(shù)目绿聘,為負(fù)值時(shí)沒(méi)有限制庸队。
MaxIdle: 空閑連接實(shí)例的最大數(shù)目箩祥,為負(fù)值時(shí)沒(méi)有限制鹃觉。Idle的實(shí)例在使用前厢钧,通常會(huì)通過(guò)org.apache.commons.pool.BasePoolableObjectFactory<T>的activateObject()方法使其變得可用鳞尔。
MaxWait: 等待可用連接的最大數(shù)目,單位毫秒(million seconds)早直。
(注:pool.getResource()方法實(shí)際調(diào)用的GenericObjectPool類(lèi)borrowObject()方法寥假,該方法會(huì)根據(jù)MaxWait變量值在沒(méi)有可用連接(idle/active)時(shí)阻塞等待知道超時(shí),具體含義參看api霞扬。)
也就是說(shuō)當(dāng)連接池中沒(méi)有active/idle的連接時(shí)糕韧,會(huì)等待maxWait時(shí)間枫振,如果等待超時(shí)還沒(méi)有可用連接,則拋出Could not get a resource from the pool異常萤彩。所以為避免這樣的錯(cuò)誤粪滤,
我們應(yīng)該根據(jù)程序?qū)嶋H情況合理設(shè)置這三個(gè)參數(shù)的值,同時(shí)在我們獲取一個(gè)連接的程序方法中也應(yīng)該合理的處理這個(gè)異常雀扶,當(dāng)沒(méi)有連接可用時(shí)杖小,等待一段時(shí)間再獲取也許是個(gè)比較好的選擇。
- java.net.SocketTimeoutException: Read timed out
異常代碼如下:
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
at redis.clients.jedis.Protocol.process(Protocol.java:79)
at redis.clients.jedis.Protocol.read(Protocol.java:131)
at redis.clients.jedis.Connection.getIntegerReply(Connection.java:188)
at redis.clients.jedis.Jedis.sismember(Jedis.java:1266)
Redis是對(duì)內(nèi)存進(jìn)行操作愚墓,速度應(yīng)該都在毫秒級(jí)予权,這是通常的認(rèn)識(shí),所以當(dāng)對(duì)Redis操作出現(xiàn)幾秒的超時(shí)時(shí)間浪册,能想象嗎扫腺?
我們還是先分析一下Jedis的源代碼吧,以sadd操作為例:
- public Long sadd(final String key, final String... members) {
- checkIsInMulti();
- client.sadd(key, members);
- return client.getIntegerReply();
- }
client是redis.clients.jedis.Client.java的實(shí)例村象,繼承關(guān)系如下:
public class Client extends BinaryClient implements Commands笆环;
public class BinaryClient extends Connection;
Connection包裝了對(duì)Redis server的socket操作厚者,命令寫(xiě)操作通過(guò)socket.getOutputStream()輸出流將命令信息發(fā)送到redis server躁劣,當(dāng)寫(xiě)完命令后要通過(guò)socket.getInputStream()的到的輸入流將
命令執(zhí)行結(jié)果返回,這中間必然會(huì)有一個(gè)命令執(zhí)行到結(jié)果返回的延時(shí)時(shí)間籍救,這就是一個(gè)Jedis調(diào)用redis命令操作所用的時(shí)間习绢。
需要說(shuō)明的是,Redis server是單線程執(zhí)行所有連接發(fā)送過(guò)來(lái)的命令的蝙昙,也就是說(shuō)不管并發(fā)中有多少個(gè)client在發(fā)送命令,redis-server端是單線程處理的梧却,并按照默認(rèn)的FIFO方式處理請(qǐng)求奇颠,
這個(gè)可在redis.conf配置文件中配置。關(guān)于redis server的詳細(xì)運(yùn)行機(jī)制參見(jiàn):http://redis.io/documentation
所以client.sadd(key, members);調(diào)用完后只是將命令信息發(fā)送到了redis server端放航,具體有沒(méi)有執(zhí)行要看redis server的負(fù)載情況烈拒。然后,通過(guò)client.getIntegerReply();等待(time out)返回結(jié)果广鳍。
Connection初始化socket時(shí)有多種選擇荆几,其中設(shè)置socket time out 的方法如下:
- public void rollbackTimeout() {
- try {
- socket.setSoTimeout(timeout);
- socket.setKeepAlive(false);
- } catch (SocketException ex) {
- throw new JedisException(ex);
- }
- }
由redis.clients.jedis.Protocol.DEFAULT_TIMEOUT = 2000;所以可以考慮把超時(shí)時(shí)間設(shè)長(zhǎng)赊时,但需要選個(gè)合理的值吨铸,否則服務(wù)可能會(huì)堵塞在這個(gè)步驟。