解决Jedis异常之 java.lang.ClassCastException: java.lang.Long cannot be cast to [B
问题描述
使用jedis sdk访问redis时,有时会抛如下异常
java.lang.ClassCastException: java.lang.Long cannot be cast to [B
at redis.clients.jedis.Connection.getBinaryBulkReply(Connection.java:259)
at redis.clients.jedis.Connection.getBulkReply(Connection.java:248)
at redis.clients.jedis.Jedis.hget(Jedis.java:674)
有时可能还会伴随着超时异常:
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:202)
at redis.clients.util.RedisInputStream.readByte(RedisInputStream.java:40)
at redis.clients.jedis.Protocol.process(Protocol.java:151)
at redis.clients.jedis.Protocol.read(Protocol.java:215)
at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
at redis.clients.jedis.Connection.getBinaryBulkReply(Connection.java:259)
at redis.clients.jedis.Connection.getBulkReply(Connection.java:248)
at redis.clients.jedis.Jedis.hget(Jedis.java:674)
原因&解决
主要有以下三类原因:
-
一个jedis连接在使用时抛出异常(如超时异常)后被返回连接池,这个连接下次使用时就可能跑类似异常,具体跟sockt buffer有上次请求数据的有关,参考链接1。
针对这种情况,需要将jedis正确close,有些文章可能会说需要调用returnBrokenResource方法,这已经是个远古方法了,至少在2.9.0以上,直接close即可,如果进行了异常捕获,则需要在finally中进行close。
-
多线程访问jedis对象
根据jedis的文档,JedisPool线程安全而Jedis非线程安全,检查代码是否有多线程逻辑。
-
redis集群资源负载重、写满等
我这里就是这个原因,代码中对redis先读后写,不停抛这个异常。如果注释掉写,只读运行没问题。注释掉读,就开始抛下面的异常,这种负载重抛异常的情况在链接2的issue也有人提及。
redis.clients.jedis.exceptions.JedisDataException: ERR at redis.clients.jedis.Protocol.processError(Protocol.java:127) at redis.clients.jedis.Protocol.process(Protocol.java:161) at redis.clients.jedis.Protocol.read(Protocol.java:215) at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340) at redis.clients.jedis.Connection.getOne(Connection.java:322) at redis.clients.jedis.ShardedJedisPipeline.sync(ShardedJedisPipeline.java:44)
最后找运维申请了个新redis就正常读写了,如果你们redis是自己的维护的,就检查下日志吧。