redis异常 ClassCastException cannot be cast to java.lang.Long

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
     * 释放连接
     * @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);
        }
    }

错误写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 里还残存着上次没有发送或者发送不完整的命令,这个时候没有做处理,直接将该连接返回到连接池,那么重用该连接执行下次命令的时候,就会将上次没有发送的命令一起发送过去,所以才会出现上面的错误“返回值类型不对”; 
所以正确的写法应该是在发送异常的时候,销毁这个连接,不能再重用!

正确写法  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
    }