1. 程式人生 > >,jedis 用連線池時超時返回值型別錯誤

,jedis 用連線池時超時返回值型別錯誤

這個是今天發現一個bug:在測試redis併發讀寫的時候(jedis作為客戶端,並使用了連線池),總是報 java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.Long
at redis.clients.jedis.Connection.getIntegerReply(Connection.java:161)
at redis.clients.jedis.Jedis.del(Jedis.java:108)
類似的錯誤,就是返回值型別和文件上的返回值型別不相符,感覺很不應該;開始懷疑是jedis實現的一個bug,後來發現一個現象,當拋一個超時異常的時候,後面就連續的出現一個類似上面的錯誤,最後終於發現了問題所在。
原先的程式碼是這樣的:

public long del(String key) {
long rt = 0L;
Jedis jedis = null;
try {
jedis = getJedis();
rt = jedis.del(key);
}
finally
{
releaseJedisInstance(jedis);
}
return rt;
}


這樣寫貌似OK,但實際上有問題,假設jedis在執行這個命令的時候,因為redis超負荷,jedis可能返回超時的異常,這個時候發生了什麼,沒有處理這個異常,直接將這個jedis的連結返回到了連線池,這樣有沒有問題呢?
檢視jedis原始碼發現他的connection中對網路輸出流做了一個封裝,其中自建了一個buffer,所以當發生異常的時候,這個buffer裡還殘存著上次沒有傳送或者傳送不完整的命令,這個時候沒有做處理,直接將該連線返回到連線池,那麼重用該連線執行下次命令的時候,就會將上次沒有傳送的命令一起傳送過去,所以才會出現上面的錯誤“返回值型別不對”;
所以正確的寫法應該是在傳送異常的時候,銷燬這個連線,不能再重用!
正確的寫法如下:

public long del(String key) {
long rt = 0L;
Jedis jedis = null;
try {
jedis = getJedis();
rt = jedis.del(key);
releaseNormalResource(jedis);
} catch (Exception e) {
returnBrokenResource(jedis);
throw Exception x;
}

return rt;
}


從上面的分析來看,我更認為是jedis實現的一個bug,當連接出現異常的時候,應該對該連線的buffer進行清空的,你認為呢?