文档章节

jedis 用连接池时超时返回值类型错误

浮躁的码农
 浮躁的码农
发布于 2016/04/05 11:25
字数 1648
阅读 1070
收藏 0
     这个是今天发现一个bug:在测试redis并发读写的时候(jedis作为客户端,并使用了连接池),总是报 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实现的一个bug,后来发现一个现象,当抛一个超时异常的时候,后面就连续的出现一个类似上面的错误,最后终于发现了问题所在。
原先的代码是这样的: 

public long del(String key) {  
        long rt = 0L;  
        Jedis jedis = null;  
        try {  
            jedis = getJedis();  
            rt = jedis.del(key);  
        }   
        finally  
        {  
            releaseJedisInstance(jedis);  
        }  
        return rt;  
    }




这样写貌似OK,但实际上有问题,假设jedis在执行这个命令的时候,因为redis超负荷,jedis可能返回超时的异常,这个时候发生了什么,没有处理这个异常,直接将这个jedis的链接返回到了连接池,这样有没有问题呢? 
查看jedis源码发现他的connection中对网络输出流做了一个封装,其中自建了一个buffer,所以当发生异常的时候,这个buffer里还残存着上次没有发送或者发送不完整的命令,这个时候没有做处理,直接将该连接返回到连接池,那么重用该连接执行下次命令的时候,就会将上次没有发送的命令一起发送过去,所以才会出现上面的错误“返回值类型不对”; 
所以正确的写法应该是在发送异常的时候,销毁这个连接,不能再重用! 
正确的写法如下: 
public long del(String key) {  
        long rt = 0L;  
        Jedis jedis = null;  
        try {  
            jedis = getJedis();  
            rt = jedis.del(key);  
                        releaseNormalResource(jedis);  
        } catch (Exception e) {  
            returnBrokenResource(jedis);  
                        throw Exception x;  
        }  
          
        return rt;  
    }




从上面的分析来看,我更认为是jedis实现的一个bug,当连接出现异常的时候,应该对该连接的buffer进行清空的,你认为呢? 


11 楼  bert82503 2015-02-09  
我们在线上也遇到了这个问题,也使用相同的处理方法,但本质上该问题是没有解决的。
因为在 ShardedJedisFactory.destroyObject(pooledShardedJedis) 中通过捕获所有的Exception,同时把异常忽略了,并未warn日志。通过这种方式虽然屏蔽了该问题,仅仅是忽略了异常,其实并未根本解决。(从下面日志就能看出来)

“从上面的分析来看,我更认为是jedis实现的一个bug,当连接出现异常的时候,应该对该连接的buffer进行清空的,你认为呢?”  赞同   

Java代码   收藏代码
  1. [2015-02-07 09:17:47] WARN  c.f.f.b.s.r.i.CustomShardedJedisFactory -quit jedis connection for server fail: xxx.xxx.xxx.xxx:xxx  
  2.   
  3. java.lang.ClassCastException: java.lang.Long cannot be cast to [B   (强制类型转换异常)  
  4.     at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:181) ~[jedis-2.6.2.jar:na]  
  5.     at redis.clients.jedis.BinaryJedis.quit(BinaryJedis.java:136) ~[jedis-2.6.2.jar:na]  
  6.     at cn.fraudmetrix.forseti.biz.service.redis.impl.CustomShardedJedisFactory.destroyObject(CustomShardedJedisFactory.java:116) ~[forseti-biz-service-1.0-SNAPSHOT.jar:na]  
  7.     at org.apache.commons.pool2.impl.GenericObjectPool.destroy(GenericObjectPool.java:848) [commons-pool2-2.0.jar:2.0]  
  8.     at org.apache.commons.pool2.impl.GenericObjectPool.invalidateObject(GenericObjectPool.java:626) [commons-pool2-2.0.jar:2.0]  
  9.     at redis.clients.util.Pool.returnBrokenResourceObject(Pool.java:83) [jedis-2.6.2.jar:na]  
  10.     at cn.fraudmetrix.forseti.biz.service.redis.impl.CustomShardedJedisPool.returnBrokenResource(CustomShardedJedisPool.java:121) [forseti-biz-service-1.0-SNAPSHOT.jar:na]  
  11.     at cn.fraudmetrix.forseti.biz.service.redis.impl.RedisServiceImpl.zadd(RedisServiceImpl.java:337) [forseti-biz-service-1.0-SNAPSHOT.jar:na]  
  12.     at cn.fraudmetrix.forseti.biz.service.redis.impl.RedisServiceImpl.zadd(RedisServiceImpl.java:319) [forseti-biz-service-1.0-SNAPSHOT.jar:na]  
  13.     ...  
  14. [2015-02-07 09:17:47] ERROR c.f.f.b.s.r.i.RedisServiceImpl -'zadd' key fail, key: xxx, score: xxx, member: xxx  
  15.   
  16. [2015-02-07 09:17:47] ERROR c.f.f.b.s.r.i.RedisServiceImpl -java.net.SocketTimeoutException: Read timed out  
  17.   
  18. redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out    (Socket读取超时异常)  
  19.     at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:201) ~[jedis-2.6.2.jar:na]     ('limit = in.read(buf);' at java.io.InputStream.read(InputStream.java:100) - 这里出现阻塞导致"Socket读取超时"!)  
  20.     at redis.clients.util.RedisInputStream.readByte(RedisInputStream.java:40) ~[jedis-2.6.2.jar:na]  
  21.     at redis.clients.jedis.Protocol.process(Protocol.java:128) ~[jedis-2.6.2.jar:na]  
  22.     at redis.clients.jedis.Protocol.read(Protocol.java:192) ~[jedis-2.6.2.jar:na]  
  23.     at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:282) ~[jedis-2.6.2.jar:na]  
  24.     at redis.clients.jedis.Connection.getIntegerReply(Connection.java:207) ~[jedis-2.6.2.jar:na]  
  25.     at redis.clients.jedis.Jedis.zadd(Jedis.java:1293) ~[jedis-2.6.2.jar:na]  
  26.     at redis.clients.jedis.ShardedJedis.zadd(ShardedJedis.java:364) ~[jedis-2.6.2.jar:na]  
  27.     at cn.fraudmetrix.forseti.biz.service.redis.impl.RedisServiceImpl.zadd(RedisServiceImpl.java:328) [forseti-biz-service-1.0-SNAPSHOT.jar:na]  
  28.     at cn.fraudmetrix.forseti.biz.service.redis.impl.RedisServiceImpl.zadd(RedisServiceImpl.java:319) [forseti-biz-service-1.0-SNAPSHOT.jar:na]  
  29.     ...  
  30. Caused by: java.net.SocketTimeoutException: Read timed out  
  31.     at java.net.SocketInputStream.socketRead0(Native Method) ~[na:1.7.0_51]  
  32.     at java.net.SocketInputStream.read(SocketInputStream.java:152) ~[na:1.7.0_51]  
  33.     at java.net.SocketInputStream.read(SocketInputStream.java:122) ~[na:1.7.0_51]  
  34.     at java.net.SocketInputStream.read(SocketInputStream.java:108) ~[na:1.7.0_51]  
  35.     at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:195) ~[jedis-2.6.2.jar:na]  
  36.     ... 38 common frames omitted  
10 楼  bert82503 2015-01-09  
HelloJimmy 写道

关注


谢谢 
9 楼  HelloJimmy 2015-01-08  
bert82503 写道
HelloJimmy 写道
bert82503 写道
javaeyes 写道
这个太悲剧了,每个请求都要try catch finally


Java代码   收藏代码
  1. poolConfig.setTestOnBorrow(true);  
  2. poolConfig.setTestOnReturn(true);  


这样就搞定问题了~



这样是OK,但是性能下降的厉害,每个验证就是一个网络操作(ping/pong),效率低,而且对redis服务还是一个负担,十分不建议!


你说得对,我们在实际生产环境中使用时也是关闭了这两个属性,在外层统一包装一层 RedisService。
我们基于 Jedis 定制实现的"Redis服务器异常(宕机)时自动摘除,恢复正常时自动添加"的功能。
https://github.com/EdwardLee03/jedis-x


关注
8 楼  bert82503 2015-01-06  
HelloJimmy 写道
bert82503 写道
javaeyes 写道
这个太悲剧了,每个请求都要try catch finally


Java代码   收藏代码
  1. poolConfig.setTestOnBorrow(true);  
  2. poolConfig.setTestOnReturn(true);  


这样就搞定问题了~



这样是OK,但是性能下降的厉害,每个验证就是一个网络操作(ping/pong),效率低,而且对redis服务还是一个负担,十分不建议!


你说得对,我们在实际生产环境中使用时也是关闭了这两个属性,在外层统一包装一层 RedisService。
我们基于 Jedis 定制实现的"Redis服务器异常(宕机)时自动摘除,恢复正常时自动添加"的功能。
https://github.com/EdwardLee03/jedis-x
7 楼  HelloJimmy 2014-12-05  
bert82503 写道
javaeyes 写道
这个太悲剧了,每个请求都要try catch finally


Java代码   收藏代码
  1. poolConfig.setTestOnBorrow(true);  
  2. poolConfig.setTestOnReturn(true);  


这样就搞定问题了~



这样是OK,但是性能下降的厉害,每个验证就是一个网络操作(ping/pong),效率低,而且对redis服务还是一个负担,十分不建议!
6 楼  bert82503 2014-11-25  
javaeyes 写道
这个太悲剧了,每个请求都要try catch finally


Java代码   收藏代码
  1. poolConfig.setTestOnBorrow(true);  
  2. poolConfig.setTestOnReturn(true);  


这样就搞定问题了~
5 楼  zhulh0927 2013-10-09  
这其实是jedis不支持多线程的原因,在你调用del等方法时,在调用方法申明前加上关键字 synchronized就可以解决问题。比如delkey调用del方法,在delkey方法前加synchronized就可以了
4 楼  restart1107 2012-11-19  
你这个是什么版本的?2 .1.0没有你里面的代码了
3 楼  javaeyes 2012-09-10  
这个太悲剧了,每个请求都要try catch finally
2 楼  HelloJimmy 2012-07-13  
Sweblish 写道
     
redis太傻了,这方面!


no,no,no.这跟redis没有关系,是jedis的问题。redis很棒的!
1 楼  Sweblish 2012-06-30  
     
redis太傻了,这方面!

本文转载自:http://hellojimmy.iteye.com/blog/1197543

浮躁的码农

浮躁的码农

粉丝 76
博文 916
码字总数 180419
作品 0
松江
程序员
私信 提问
redis学习笔记二

http://redisbook.readthedocs.io/en/latest/ redis为什么会有高并发问题 redis的出身决定 redis是一种单线程机制的nosql数据库,基于key-value,数据可持久化落盘。由于单线程所以redis本身...

writeademo
2016/09/14
69
0
Redis客户端常见异常分析

一.无法从连接池获取到连接 JedisPool中的Jedis对象个数是有限的,默认是8个。这里假设使用的默认配置,如果有8个Jedis对象被占用,并且没有归还,如果调用者还要从JedisPool中借用Jedis,就...

xpttxsok
2016/11/30
157
0
Redis客户端常见异常分析

一.无法从连接池获取到连接 JedisPool中的Jedis对象个数是有限的,默认是8个。这里假设使用的默认配置,如果有8个Jedis对象被占用,并且没有归还,如果调用者还要从JedisPool中借用Jedis,就...

杀神之疯子
2017/06/02
0
0
Jedis的类型转换异常深究

1 类型转换异常场景 我们在使用Jedis的时候,经常会出现类型转换异常,有如下情况: - 多线程环境 Jedis是线程不安全的,如果存在多线程使用同一个Jedis,就会出现类型转换异常网上也流传着很...

乒乓狂魔
2016/08/24
3K
16
redis - java 客户端使用

命令参考: http://redisdoc.com/ 教程: http://redisinaction.com/ 1.准备.下载jar 包 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.3</v......

Canaan_
2016/08/12
133
0

没有更多内容

加载失败,请刷新页面

加载更多

Archiva 不小心删掉了管理员权限怎么办

Archiva 的界面和 UI 比较容易出问题。 在添加用户和为用户进行权限修改的时候,不小心连 admin 这个用户的权限都删掉了。 这个时候应该如何恢复 admin 这个用户的权限? 这个时候你可以尝试...

honeymoose
56分钟前
7
0
Java8新特性之空指针异常的克星Optional类

Java8新特性系列我们已经介绍了Stream、Lambda表达式、DateTime日期时间处理,最后以“NullPointerException” 的克星Optional类的讲解来收尾。 背景 作为开发人员每天与NullPointerExceptio...

程序新视界
今天
5
0
OSChina 周四乱弹 —— 福布斯终身秃头奖

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @宇辰OSC :分享如是我闻的单曲《地藏经上卷》: 如是我闻#今日歌曲推荐# 《地藏经上卷》- 如是我闻 手机党少年们想听歌,请使劲儿戳(这里) ...

小小编辑
今天
449
11
Mybatis Plus删除

/** @author beth @data 2019-10-17 00:30 */ @RunWith(SpringRunner.class) @SpringBootTest public class DeleteTest { @Autowired private UserInfoMapper userInfoMapper; /** 根据id删除......

一个yuanbeth
今天
7
0
总结

一、设计模式 简单工厂:一个简单而且比较杂的工厂,可以创建任何对象给你 复杂工厂:先创建一种基础类型的工厂接口,然后各自集成实现这个接口,但是每个工厂都是这个基础类的扩展分类,spr...

BobwithB
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部