用了很久redis了,觉得自己对redis应该算是比较了解了吧。谁知前段时间遇到redis的一个问题,困扰了我几个月一直没有解决:

redis断开重新连接 redis connect reset by peer_redis

奇怪的是这个错误并不是一开始就出现的,而是运行上一段时间后才会出现。重启Tomcat后又会恢复正常。

一开始以为是网络的问题,结果换成使用本机的redis还是一样的问题。后来怀疑是机器性能的问题,但经测试无论使用性能非常差的机器还是性能非常好的机器均出现同样的问题。

于是,只好静下心来从程序入手。我的核心程序如下:

private Jedis jedis;
        private JedisPool jedisPool;
        private ShardedJedis shardedJedis;
        private ShardedJedisPool shardedJedisPool;
        private int _RedisKeyExpiredSeconds = 3600;

        public RedisCache(String strRedisServer, int iRedisPort, int lRedisExpiredSeconds){
	    _RedisServer = strRedisServer;
	    _RedisPort = iRedisPort;
	    _RedisKeyExpiredSeconds = lRedisExpiredSeconds;
	    initialPool(); 
            initialShardedPool(); 
            shardedJedis = shardedJedisPool.getResource(); 
            jedis = jedisPool.getResource(); 
	}
	public void init(){
	    initialPool(); 
            initialShardedPool(); 
            shardedJedis = shardedJedisPool.getResource(); 
            jedis = jedisPool.getResource();
	}
	public String getKey(String key){
	    try{
		return shardedJedis.get(key);
	    }catch(Exception e){
                System.out.println(e.getMessage());
	    }
	    return null;
	}
	public void putKey(String key, String value){
	    try{
		shardedJedis.set(key, value);
		shardedJedis.expire(key, _RedisKeyExpiredSeconds);
    	    }catch(Exception e){
		System.out.println(e.getMessage());
	    }
	}
        public void removeKey(String key){
	    try{
		long r = shardedJedis.del(key);
	    }catch(Exception e){
		System.out.println(e.getMessage());
	    }
	}

每次总是在shardedJedis.set(key, value);和long r = shardedJedis.del(key);这两个位置出错。

经网上查阅,大概原因是因为连接池被占满造成的。可是,我就一个客户端,而且对于redis操作频率低的实在是可以忽略,怎么就把连接池占满了哪?这种可能性直接被忽略。

还有一种说法,说是使用完redis没有释放连接池资源。于是我在每次操作完redis后都调用了方法returnResource(),定义如下:

private void returnResource(ShardedJedis shardedJedis, boolean broken) {
         try{
             if (broken) {
	         shardedJedisPool.returnBrokenResource(shardedJedis);
	     } else {
	         shardedJedisPool.returnResource(shardedJedis);
	     }
         }catch(Exception e){}
     }

这样试了下,还是不行。此时陷入僵局~~~

过了一会儿,无奈之下我忽然想出了一个“损招”:能否将sharedJedisPool改为单例模式?这样不就不会导致连接池被用光了吗?

于是,我把sharedJedis声明为static变量:

private static ShardedJedis shardedJedis;

 问题照旧!!!

后来,不知从哪里来的零感,我试着在每次使用shardedJedis前都从连接池里获取一次,结果居然不再报错了!!!

代码如下:

private Jedis jedis;
        private JedisPool jedisPool;
        private ShardedJedis shardedJedis;
        private ShardedJedisPool shardedJedisPool;
        private int _RedisKeyExpiredSeconds = 3600;

        public RedisCache(String strRedisServer, int iRedisPort, int lRedisExpiredSeconds){
	    _RedisServer = strRedisServer;
	    _RedisPort = iRedisPort;
	    _RedisKeyExpiredSeconds = lRedisExpiredSeconds;
	    initialPool(); 
            initialShardedPool(); 
            shardedJedis = shardedJedisPool.getResource(); 
            jedis = jedisPool.getResource(); 
	}
	public void init(){
	    initialPool(); 
            initialShardedPool(); 
            shardedJedis = shardedJedisPool.getResource(); //先从连接池里获取资源
            jedis = jedisPool.getResource();
	}
	public String getKey(String key){
	    Boolean blBroken = false;
	    try{
		shardedJedis = shardedJedisPool.getResource();  //先从连接池里获取资源
		return shardedJedis.get(key);
       	    }catch(Exception e){
		blBroken = true;
	    }finally{
		this.returnResource(shardedJedis, blBroken);
	    }
	    return null;
	}
	public void putKey(String key, String value){
	    Boolean blBroken = false;
	    try{
		shardedJedis = shardedJedisPool.getResource();  //先从连接池里获取资源
		shardedJedis.set(key, value);
		shardedJedis.expire(key, _RedisKeyExpiredSeconds);
	    }catch(Exception e){
		System.out.println(e.getMessage());
		blBroken = true;
	    }finally{
		this.returnResource(shardedJedis, blBroken);
	    }
	}
	
	@Override
	public void putKey(String key, String value, int expireSeconds) {
	    Boolean blBroken = false;
	    try{
		shardedJedis = shardedJedisPool.getResource();  //先从连接池里获取资源
		shardedJedis.set(key, value);
		shardedJedis.expire(key, expireSeconds);
	    }catch(Exception e){
		blBroken = true;
		System.out.println(e.getMessage());
	    }finally{
		this.returnResource(shardedJedis, blBroken);
	    }
	}
	
	public void removeKey(String key){
	    Boolean blBroken = false;
	    try{
		shardedJedis = shardedJedisPool.getResource();  //先从连接池里获取资源
		long r = shardedJedis.del(key);
	    }catch(Exception e){
		blBroken = true;
		System.out.println(e.getMessage());
	    finally{
		this.returnResource(shardedJedis, blBroken);
	    }
	}
	private void initialPool() { 
            JedisPoolConfig config = new JedisPoolConfig(); 
            config.setMaxTotal(20);
            config.setMaxIdle(5); 
            config.setMaxWaitMillis(10001);
            config.setTestOnBorrow(false); 
        
            jedisPool = new JedisPool(config, _RedisServer, _RedisPort);
        }
    
        private void initialShardedPool() { 

            JedisPoolConfig config = new JedisPoolConfig(); 
            config.setMaxTotal(20); 
            config.setMaxIdle(5); 
            config.setMaxWaitMillis(1000l); 
            config.setTestOnBorrow(false); 

            List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>(); 

            shards.add(new JedisShardInfo(_RedisServer, _RedisPort, "master"));

            shardedJedisPool = new ShardedJedisPool(config, shards); 
        }

        private void returnResource(ShardedJedis shardedJedis, boolean broken) {
            try{
	    	 if (broken) {
	             shardedJedisPool.returnBrokenResource(shardedJedis);
	         } else {
	             shardedJedisPool.returnResource(shardedJedis);
	         }
             }catch(Exception e){}
         }

事后分析,我觉得问题应该是出在了没有用资源池,从而无法获得资源。请大家也提提自己的理解。