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,有可能最终导致负数(其实是与实际活动数不匹配),从而影响到原始设定的最大连接数会不管用。