/** * 释放连接 * @param pool * @param jedis * @param <T> */ public static <T> void releaseConnection(Pool<T> pool, T jedis) { if (pool != null && jedis != null) { pool.returnResource(jedis); } } /** * 超时等异常时清空该对象上次执行命令的结果缓存 * @param pool * @param jedis * @param <T> */ public static <T> void clearBuffer(Pool<T> pool, T jedis) { if (pool != null && jedis != null) { pool.returnBrokenResource(jedis); } }
错误写法
public Long append(String key, String value) { Long l = null; List<JedisPool> pools = cluster.getWriteRedisPool(key); for(JedisPool pool : pools){ Jedis jedis = null; try { jedis = pool.getResource(); l = jedis.append(key, value); } catch (Exception e) { throw new RedisException(e); } finally{ ReleaseConnectionUtils.releaseConnection(pool,jedis); } } return l; }
程序执行上图方法时偶尔抛出异常:
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在执行这个命令的时候,因为redis超负荷,jedis可能返回超时的异常,这个时候发生了什么,没有处理这个异常,直接将这个jedis的链接返回到了连接池,这样有没有问题呢?
查看jedis源码发现他的connection中对网络输出流做了一个封装,其中自建了一个buffer,所以当发生异常的时候,这个buffer里还残存着上次没有发送或者发送不完整的命令,这个时候没有做处理,直接将该连接返回到连接池,那么重用该连接执行下次命令的时候,就会将上次没有发送的命令一起发送过去,所以才会出现上面的错误“返回值类型不对”;
所以正确的写法应该是在发送异常的时候,销毁这个连接,不能再重用!
正确写法
public Long append(String key, String value) { Long l = null; List<JedisPool> pools = cluster.getWriteRedisPool(key); for(JedisPool pool : pools){ Jedis jedis = null; try { jedis = pool.getResource(); l = jedis.append(key, value); } catch (Exception e) { ReleaseConnectionUtils.clearBuffer(pool,jedis); throw new RedisException(e); } finally{ ReleaseConnectionUtils.releaseConnection(pool,jedis); } } return l; }