`
HelloJimmy
  • 浏览: 35543 次
  • 性别: Icon_minigender_1
  • 来自: LostTemple
社区版块
存档分类
最新评论

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

阅读更多
     这个是今天发现一个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进行清空的,你认为呢?”  赞同 

[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

java.lang.ClassCastException: java.lang.Long cannot be cast to [B   (强制类型转换异常)
	at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:181) ~[jedis-2.6.2.jar:na]
	at redis.clients.jedis.BinaryJedis.quit(BinaryJedis.java:136) ~[jedis-2.6.2.jar:na]
	at cn.fraudmetrix.forseti.biz.service.redis.impl.CustomShardedJedisFactory.destroyObject(CustomShardedJedisFactory.java:116) ~[forseti-biz-service-1.0-SNAPSHOT.jar:na]
	at org.apache.commons.pool2.impl.GenericObjectPool.destroy(GenericObjectPool.java:848) [commons-pool2-2.0.jar:2.0]
	at org.apache.commons.pool2.impl.GenericObjectPool.invalidateObject(GenericObjectPool.java:626) [commons-pool2-2.0.jar:2.0]
	at redis.clients.util.Pool.returnBrokenResourceObject(Pool.java:83) [jedis-2.6.2.jar:na]
	at cn.fraudmetrix.forseti.biz.service.redis.impl.CustomShardedJedisPool.returnBrokenResource(CustomShardedJedisPool.java:121) [forseti-biz-service-1.0-SNAPSHOT.jar:na]
	at cn.fraudmetrix.forseti.biz.service.redis.impl.RedisServiceImpl.zadd(RedisServiceImpl.java:337) [forseti-biz-service-1.0-SNAPSHOT.jar:na]
	at cn.fraudmetrix.forseti.biz.service.redis.impl.RedisServiceImpl.zadd(RedisServiceImpl.java:319) [forseti-biz-service-1.0-SNAPSHOT.jar:na]
	...
[2015-02-07 09:17:47] ERROR c.f.f.b.s.r.i.RedisServiceImpl -'zadd' key fail, key: xxx, score: xxx, member: xxx

[2015-02-07 09:17:47] ERROR c.f.f.b.s.r.i.RedisServiceImpl -java.net.SocketTimeoutException: Read timed out

redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out    (Socket读取超时异常)
	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读取超时"!)
	at redis.clients.util.RedisInputStream.readByte(RedisInputStream.java:40) ~[jedis-2.6.2.jar:na]
	at redis.clients.jedis.Protocol.process(Protocol.java:128) ~[jedis-2.6.2.jar:na]
	at redis.clients.jedis.Protocol.read(Protocol.java:192) ~[jedis-2.6.2.jar:na]
	at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:282) ~[jedis-2.6.2.jar:na]
	at redis.clients.jedis.Connection.getIntegerReply(Connection.java:207) ~[jedis-2.6.2.jar:na]
	at redis.clients.jedis.Jedis.zadd(Jedis.java:1293) ~[jedis-2.6.2.jar:na]
	at redis.clients.jedis.ShardedJedis.zadd(ShardedJedis.java:364) ~[jedis-2.6.2.jar:na]
	at cn.fraudmetrix.forseti.biz.service.redis.impl.RedisServiceImpl.zadd(RedisServiceImpl.java:328) [forseti-biz-service-1.0-SNAPSHOT.jar:na]
	at cn.fraudmetrix.forseti.biz.service.redis.impl.RedisServiceImpl.zadd(RedisServiceImpl.java:319) [forseti-biz-service-1.0-SNAPSHOT.jar:na]
	...
Caused by: java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method) ~[na:1.7.0_51]
	at java.net.SocketInputStream.read(SocketInputStream.java:152) ~[na:1.7.0_51]
	at java.net.SocketInputStream.read(SocketInputStream.java:122) ~[na:1.7.0_51]
	at java.net.SocketInputStream.read(SocketInputStream.java:108) ~[na:1.7.0_51]
	at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:195) ~[jedis-2.6.2.jar:na]
	... 38 common frames omitted
10 楼 bert82503 2015-01-09  
HelloJimmy 写道

关注


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


        poolConfig.setTestOnBorrow(true);
        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


        poolConfig.setTestOnBorrow(true);
        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


        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(true);


这样就搞定问题了~



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


        poolConfig.setTestOnBorrow(true);
        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太傻了,这方面!

相关推荐

    jedis连接池配置demo

    本教程将详细介绍如何配置Jedis的连接池——ShardedJedisPool,这对于初学者理解和实践Redis连接池的使用至关重要。 首先,我们需要引入Jedis和Apache Commons Pool2的依赖库。在Maven项目中,可以在pom.xml文件中...

    jedis-2.9.0.jar及连接池分享

    使用Jedis时,可以通过连接池来管理连接,例如使用`GenericObjectPoolConfig`配置连接池参数,如最大活动连接数、最大空闲连接数、最大等待时间等。然后创建`JedisPool`实例,指定Redis服务器地址、端口、密码等...

    redis连接池jar jedis+common

    4. **使用JedisPool**:在需要使用Jedis时,从池中borrow一个实例,执行操作后返回给连接池,而不是直接关闭。 四、连接池的优势 1. **性能提升**:避免了频繁创建和销毁连接的开销,提高了应用的响应速度。 2. **...

    详谈Jedis连接池的使用

    Jedis连接池的使用 Jedis连接池是Redis客户端Jedis的连接池实现,用于管理Redis连接的创建、释放和复用。通过使用Jedis连接池,可以提高Redis的访问性能和可靠性。在本文中,我们将详细讨论Jedis连接池的使用,包括...

    java连接redis/jedis连接池/jedis相关工具/jedis对象存取

    此外,开发过程中,使用Jedis时,要时刻注意错误处理和资源管理,确保每次操作后正确关闭Jedis实例,防止资源泄漏。对于大型项目,还可以考虑使用更高级的客户端,如Lettuce,它支持异步操作和更复杂的连接管理策略...

    Spring mvc整合redis实例(redis连接池)

    `JedisPoolConfig`用来设置连接池的参数,如最大连接数、最大空闲连接数、连接超时时间等。`JedisConnectionFactory`则用于创建与Redis服务器的连接。 ```xml <bean id="jedisPoolConfig" class="redis.clients....

    jedis-2.9.0.jar和commons-pool2-2.6.0.jar下载(jedis连接redis数据库)

    3. **连接池配置**: 使用Apache Commons Pool配置JedisPool,这将管理连接的创建、复用和回收。 ```java JedisPoolConfig poolConfig = new JedisPoolConfig(); // 设置最大连接数、最大空闲连接等参数 Jedis...

    Jedis出现connection timeout问题解决方法(JedisPool连接池使用实例)

    4. `whenExhaustedAction`:当池中的连接都被使用时,定义处理策略。默认有三种: - WHEN_EXHAUSTED_FAIL:拒绝新的请求并抛出异常。 - WHEN_EXHAUSTED_BLOCK:阻塞请求,直到有连接可用,或者等待`maxWait`时间后...

    jedis(java连接redis)

    4. **连接池**:为了提高性能和资源管理,建议使用Jedis连接池。 ```java JedisPoolConfig config = new JedisPoolConfig(); JedisPool pool = new JedisPool(config, "localhost"); Jedis jedis = pool.get...

    redis工具类以及redis 连接池配置

    创建jedis池配置实例,redis各种crud方法,包括使用主从同步,读写分离。工具类中包括存放hash表键值对,键值对以map的方式储存,删除键值对,永久存放键值对,设置过期时间,需要的直接去gitHab上看...

    redis的Java客户端jedis池的介绍及使用.rar

    当需要使用时,可以从池中获取一个已建立连接的Jedis对象,用完后归还回池,而不是直接关闭连接。这样可以避免频繁创建和销毁连接带来的性能开销,提高系统效率。 二、JedisPool配置 配置JedisPool时,主要涉及...

    Jedis简明教程

    本教程将深入探讨Jedis的连接池使用方法以及数据类型操作,同时也简单介绍Jedis的事务处理。 **一、Jedis连接池** 在实际应用中,频繁地创建和关闭Jedis连接会消耗大量的系统资源,因此使用连接池进行管理是必要的...

    jedis-4.3.1

    对于许多应用程序,最好使用连接池。实例化 Jedis 连接池: JedisPool pool = new JedisPool("localhost", 6379); 以下是如何在try-with-resources块中运行单个SET命令: try (Jedis jedis = pool.getResource()) {...

    jedis2.9-common-pool

    4. **线程安全**:Jedis设计为线程不安全,推荐每个线程都使用独立的Jedis实例,或者配合连接池使用,以避免线程间的数据冲突。 5. **API友好**:Jedis的API设计简洁明了,易于理解和使用,大大降低了开发者的学习...

    redis连接池

    当应用程序需要访问数据库时,它可以从池中获取一个已经建立好的连接,使用完毕后再归还到池中,而不是直接关闭。这样避免了频繁地创建和销毁连接,减少了系统资源的消耗,提升了系统响应速度。 在Ecmall框架中,...

    官网jedis-2.9.0+commons-pool2-2.5.0 jar包

    然后,他们可以按照Jedis的API文档来配置和使用Jedis连接池,例如设置最大连接数、超时时间等参数,以及执行Redis操作。 以下是一个简单的Jedis连接池配置示例: ```java import redis.clients.jedis.JedisPool; ...

    Jedis-Common-Pool

    **Jedis连接池** 使用Apache Commons Pool 2的配置和管理机制,可以配置连接池的最大连接数、最小连接数、超时时间等参数。通过连接池,Jedis可以有效地处理高并发场景,避免因创建过多连接导致的资源浪费。同时,...

    jedis-2.9.0.jar+commons-pool2-2.4.2.jar

    3. **获取和归还连接**:在需要操作Redis时,从连接池中借用一个Jedis实例,使用`JedisPool.getResource()`。完成操作后,必须通过`Jedis.close()`归还连接,确保资源得到正确释放。 4. **异常处理**:在使用Jedis...

Global site tag (gtag.js) - Google Analytics