通過(guò)jedis客戶端連接redis寫入報(bào)"JedisConnectionException: java.net.SocketException: Software caused connection

問題現(xiàn)象(jedis-2.1.0.jar)

基于JedisPool管理Jedis對(duì)象,通過(guò)get方法獲取值,出現(xiàn)key對(duì)應(yīng)的value值錯(cuò)誤,例如:

K V

a a

Jedis.get(“a”)==’b’

通過(guò)獲取key為a的值,但獲取了值b來(lái)。

同一套代碼的項(xiàng)目,分別部署在兩個(gè)不同的應(yīng)用集群,其中一個(gè)集群出現(xiàn)這種問題,而另一個(gè)集群卻沒有出現(xiàn)。

問題分析

通過(guò)表象可以看出,應(yīng)該是鏈接池的Jedis對(duì)象鏈接出現(xiàn)錯(cuò)亂而導(dǎo)致的。而兩個(gè)集群中的其中一個(gè)集群出現(xiàn),這兩個(gè)集群的唯一區(qū)別就是網(wǎng)絡(luò)環(huán)境不一樣,所以連接Redis服務(wù)器的網(wǎng)絡(luò)是有差別的。

問題思考

根據(jù)以上信息,可以大致判斷出應(yīng)該跟網(wǎng)速和JedisPool鏈接池的超時(shí)時(shí)間(500毫秒)設(shè)置有關(guān)。那接下來(lái)的問題是,如果網(wǎng)絡(luò)差的集群,出現(xiàn)redis連接超時(shí),那么Jedis為什么會(huì)錯(cuò)誤呢?是否在連接池的配置不當(dāng)知道呢?

問題發(fā)現(xiàn)

帶著以上的疑問,繼續(xù)Google和百度相關(guān)資料,結(jié)果發(fā)現(xiàn)returnBrokenResource這個(gè)方法。通過(guò)資料查找和對(duì)JedisPool的源碼分析,此方法是銷毀異常Jedis連接的。如果Jedis鏈接發(fā)現(xiàn)異常(如連接超時(shí)),不對(duì)異常連接銷毀的話,會(huì)有數(shù)據(jù)緩存問題。

異常流程:

重現(xiàn)問題測(cè)試代碼(設(shè)置1ms的readTimeOut時(shí)間,以便問題重現(xiàn)):

1 package test; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.Properties; 6 import java.util.Random; 7 import org.apache.commons.lang.StringUtils; 8 import redis.clients.jedis.Jedis; 9 import redis.clients.jedis.JedisPool; 10 import redis.clients.jedis.JedisPoolConfig; 11 12 public class RedisTest implements Runnable{ 13 14 public static JedisPool pool = null; 15 16 static { 17 try { 18 JedisPoolConfig config = new JedisPoolConfig(); 19 config.setMaxActive(100); 20 config.setMaxIdle(10); 21 config.setMaxWait(1000); 22 config.setTestOnBorrow(false); 23 config.setTestOnReturn(false); 24 config.setTestWhileIdle(true); 25 config.setTimeBetweenEvictionRunsMillis(30000); 26 config.setNumTestsPerEvictionRun(10); 27 config.setMinEvictableIdleTimeMillis(60000); 28 pool = new JedisPool(config, "192.168.22.213", 6379,1); 29 } catch (Exception e) { 30 System.out.println("【jedispool init error】"); 31 } 32 } 33 34 public void run() { 35 36 Jedis jedis = null; 37 String result = ""; 38 int i = new Random().nextInt(1000); 39 40 try{ 41 jedis=pool.getResource(); 42 result = jedis.get("T"+i); 43 44 if(StringUtils.isNotEmpty(result) && !result.equals("T"+i)){ 45 System.out.println(result+"!=T"+i); 46 } 47 48 }catch(Exception e){ 49 System.out.println(jedis+e.toString()); 50 51 }finally{ 52 if(jedis!=null){ 53 pool.returnResource(jedis); 54 } 55 } 56 57 } 58 59 /** 60 * 模擬2000線程并發(fā) 61 */ 62 public static void main(String[] args) throws Exception { 63 64 for(int i=0;i<2000;i++){ 65 new Thread(new RedisTest()).start(); 66 } 67 } 68 }

執(zhí)行結(jié)果:

……

T50!=T47

redis.clients.jedis.Jedis@1295fe8redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

T56!=T94

redis.clients.jedis.Jedis@151b0a5redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

T717!=T380

redis.clients.jedis.Jedis@131303fredis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

redis.clients.jedis.Jedis@602b6bredis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

T204!=T787

T474!=T763

T163!=T542

T552!=T60

T604!=T820

T733!=T624

redis.clients.jedis.Jedis@131303fredis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

redis.clients.jedis.Jedis@d56b37redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

redis.clients.jedis.Jedis@151b0a5redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

redis.clients.jedis.Jedis@1295fe8redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

T784!=T948

T440!=T672

T97!=T867

……

以上結(jié)果出現(xiàn)許多鍵值不對(duì)應(yīng)的情況。

解決方案

當(dāng)Jedis讀超時(shí)時(shí),把此實(shí)例銷毀,以免造成后續(xù)傷害。

銷毀異常Jedis有三種方法:

方法1:加入紅色代碼,當(dāng)讀取Redis數(shù)據(jù)時(shí)任何異常都拋棄此Jedis實(shí)例

try{ jedis=pool.getResource(); result = jedis.get("T"+i); if(StringUtils.isNotEmpty(result) && !result.equals("T"+i)){ System.out.println(result+"!=T"+i); } }catch(Exception e){ System.out.println(jedis+e.toString()); if(jedis!=null){ pool.returnBrokenResource(jedis); } }finally{ if(jedis!=null){ pool.returnResource(jedis); } }

方法2:配置JedisPool的TestOnBorrow為true

config.setTestOnBorrow(true)

方法3:配置JedisPool的TestOnReturn為true

config.setTestOnReturn(true)

總結(jié)

其實(shí)以上三種方法原理都是一樣,就是檢查Jedis的有效性,銷毀異常Jedis鏈接實(shí)例。只是檢查的時(shí)間不一樣。

而導(dǎo)致量應(yīng)用集群中其中之一出現(xiàn),可以定位為有問題集群到Redis集群服務(wù)器的網(wǎng)速比正常集群的差(500ms超時(shí)限制)

方法1是在發(fā)生時(shí)檢驗(yàn)銷毀;

方法2是在從連接池獲取Jedis實(shí)例時(shí)檢查;

截圖源碼來(lái)自package org.apache.commons.pool.impl.GenericObjectPool

方法3是在歸還Jedis實(shí)例給連接池時(shí)檢查;

截圖源碼來(lái)自package org.apache.commons.pool.impl.GenericObjectPool

以上三種檢查連接有效性方法都是一致:

oolean isNormal = false

try{

isNormal = (jedis.isConnected()) && (jedis.ping().equals("PONG"))

}catch(Exception e){

isNormal = false

注1,三種方法可以同時(shí)使用,但需要在檢查性能消耗和功能穩(wěn)定性之間衡量。

文章更新時(shí)間:2016-07-01

更新內(nèi)容:當(dāng)我把jedis更新到2.8.1的時(shí)候使用returnBrokenResource和returnResource顯示方法過(guò)期,原因是Jedis類重寫了這個(gè)close方法,原本是沒有的,它在close已經(jīng)幫你判斷好,源碼如下所示:

ublic void close() { if (this.dataSource != null) { if (this.client.isBroken()) this.dataSource.returnBrokenResource(this); else this.dataSource.returnResource(this); } else this.client.close(); }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 使用redis也有端時(shí)間了,現(xiàn)在講開發(fā)中遇到的幾個(gè)常見異??偨Y(jié)如下: 一、通過(guò)JedisPool類實(shí)例獲取getR...
    托普帝國(guó)_rachel閱讀 14,147評(píng)論 0 2
  • NOSQL類型簡(jiǎn)介鍵值對(duì):會(huì)使用到一個(gè)哈希表,表中有一個(gè)特定的鍵和一個(gè)指針指向特定的數(shù)據(jù),如redis,volde...
    MicoCube閱讀 4,151評(píng)論 2 27
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評(píng)論 19 139
  • 感恩!今天是父親節(jié),給愛喝綠茶的老爸送兩袋,祝親愛的老爸節(jié)日快樂!謝謝!謝謝!謝謝! 感恩!喜歡聽解讀是考核錄音及...
    梧桐70閱讀 275評(píng)論 0 0
  • 吵架了,離婚! 男人沉默著,無(wú)處發(fā)泄的情緒就像手中的煙頭,一絲絲燃燒著。 女人啜泣著,雜亂的心緒是淌在臉頰的...
    一片梧桐樹葉閱讀 180評(píng)論 0 0

友情鏈接更多精彩內(nèi)容