首页 > 代码库 > ShardedJedisPool 中可用连接数的小bug

ShardedJedisPool 中可用连接数的小bug

ShardedJedisPool中,returnBrokenResource() 及 returnResource() ,为施放资源、关闭连接的方法,若重复调用,导致 _numActive 当前活动数一直递减,会出现负数的情况。

假如在一个方法中设置了三个jedis连接,在获取第一或第二个连接时出现异常,在抛出异常或者finally中总是施放这三个资源,会导致池中的连接连续施放三次,从而变成负数。

这样会出现连接池最大连接数配置无效的情况。

 

以下片段代码:

public class RedisUtil {
    public static ShardedJedisPool pool;
    static {
        JedisPoolConfig config = new JedisPoolConfig();// Jedis池配置
        config.setMaxActive(2);// 最大活动的对象个数
        config.setMaxIdle(1000 * 60);// 对象最大空闲时间
        config.setMaxWait(1000 * 3);// 获取对象时最大等待时间
        config.setTestOnBorrow(true);
        String hostA = "192.168.0.99";
        int portA = 6380;
        List<JedisShardInfo> jdsInfoList =new ArrayList<JedisShardInfo>(1);
        JedisShardInfo infoA = new JedisShardInfo(hostA, portA);
        jdsInfoList.add(infoA);
        pool = new ShardedJedisPool(config, jdsInfoList);
    }
    
    public static void testRedis() {
        
        ShardedJedis jedis = null;
        ShardedJedis jedis1 = null;
        ShardedJedis jedis2 = null;
        try {
            // 从池中获取三次连接
            jedis = pool.getResource();
            jedis1 = pool.getResource();
            jedis2 = pool.getResource();
            
            String value = jedis.get("wuse");
            String value1 = jedis1.get("wuse");
            if (null == value || "".equals(value)) {
                jedis.set("wuse", "testWuse");
                jedis.expire("wuse", 20);
            }else {
                System.out.println(value);
            }
        } catch (Exception e) {
            e.printStackTrace();
            
            // 异常时关闭连接,此处可以注释
        } finally {
            pool.returnBrokenResource(jedis);
            pool.returnBrokenResource(jedis1);
            pool.returnBrokenResource(jedis2);
        }
    }
}

 

比如,设置的最大连接数为3,当第一次获取连接1和连接2的时候,没有问题,获取第三个连接的时候,由于最大连接数为2,所以抛异常

走finally之后,释放掉三个连接资源,这时候,pool连接池的当前活动数竟然为-1

所以,第二次再调用testRedis()方法时,由于之前pool的活动数为-1,这次三个连接都能获取成功,不抛异常。

 

-------------------------------------------------------------------------------

ShardedJedisPool类本身继承Pool类,Pool类中用了org.apache.commons.pool.impl.GenericObjectPool类来当做连接池,而Pool类初始化时,需要传入PoolableObjectFactory工厂类,在ShardedJedisPool类中自定义了一个继承BasePoolableObjectFactory类的工厂类ShardedJedisFactory,而ShardedJedisFactory类中的销毁方法destroyObject()方法中,从传进来的ShardedJedis对象里获取了镜像连接,继承的最顶层Sharded类中的getAllShards()方法,实际上只是通过Collections工具类获取了resources的value集合的镜像,所以实际上也就是说只是意义上释放了连接。 

而ShardedJedisPool类中的returnBrokenResource()方法,实际上调用的是GenericObjectPool类的invalidateObject()方法,而每次调用后,总会再去做 _numActive --,也就是说,每次调用,当前活动数都会减1,有可能最终导致负数(其实是与实际活动数不匹配),从而影响到原始设定的最大连接数会不管用。