JedisPool异常Jedis链接处理 - wc的一些事一些情

标签: jedispool 异常 jedis | 发表时间:2015-10-15 18:02 | 作者:wc的一些事一些情
出处:

 

问题现象

 

基于JedisPool管理Jedis对象,通过get方法获取值,出现key对应的value值错误,例如:

K V

a a

b b

Jedis.get(“a”)==’b’;

通过获取key为a的值,但获取了值b来。

同一套代码的项目,分别部署在两个不同的应用集群,其中一个集群出现这种问题,而另一个集群却没有出现。

 

问题分析

 

通过表象可以看出,应该是链接池的Jedis对象链接出现错乱而导致的。而两个集群中的其中一个集群出现,这两个集群的唯一区别就是网络环境不一样,所以连接Redis服务器的网络是有差别的。

 

问题思考

 

根据以上信息,可以大致判断出应该跟网速和JedisPool链接池的超时时间(500毫秒)设置有关。那接下来的问题是,如果网络差的集群,出现redis连接超时,那么Jedis为什么会错误呢?是否在连接池的配置不当知道呢?

 

问题发现

带着以上的疑问,继续Google和百度相关资料,结果发现returnBrokenResource这个方法。通过资料查找和对JedisPool的源码分析,此方法是销毁异常Jedis连接的。如果Jedis链接发现异常(如连接超时),不对异常连接销毁的话,会有数据缓存问题。

异常流程:

重现问题测试代码:

1 package test;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.util.Properties;
6 import java.util.Random;
7 import org.apache.commons.lang.StringUtils;
8 import redis.clients.jedis.Jedis;
9 import redis.clients.jedis.JedisPool;
10 import redis.clients.jedis.JedisPoolConfig;
11
12 public class RedisTest implements Runnable{
13
14 public static JedisPool pool = null;
15
16 static {
17 try {
18 JedisPoolConfig config = new JedisPoolConfig();
19 config.setMaxActive(100);
20 config.setMaxIdle(10);
21 config.setMaxWait(1000);
22 config.setTestOnBorrow(false);
23 config.setTestOnReturn(false);
24 config.setTestWhileIdle(true);
25 config.setTimeBetweenEvictionRunsMillis(30000);
26 config.setNumTestsPerEvictionRun(10);
27 config.setMinEvictableIdleTimeMillis(60000);
28 pool = new JedisPool(config, "192.168.22.213", 6379,1);
29 } catch (Exception e) {
30 System.out.println("【jedispool init error】");
31 }
32 }
33
34 public void run() {
35
36 Jedis jedis = null;
37 String result = "";
38 int i = new Random().nextInt(1000);
39
40 try{
41 jedis=pool.getResource();
42 result = jedis.get("T"+i);
43
44 if(StringUtils.isNotEmpty(result) && !result.equals("T"+i)){
45 System.out.println(result+"!=T"+i);
46 }
47
48 }catch(Exception e){
49 System.out.println(jedis+e.toString());
50
51 }finally{
52 if(jedis!=null){
53 pool.returnResource(jedis);
54 }
55 }
56
57 }
58
59 /**
60 * 模拟2000线程并发
61 */
62 public static void main(String[] args) throws Exception {
63
64 for(int i=0;i<2000;i++){
65 new Thread(new RedisTest()).start();
66 }
67 }
68 }

执行结果:

……

T50!=T47

redis.clients.jedis.Jedis@1295fe8redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

T56!=T94

redis.clients.jedis.Jedis@151b0a5redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

T717!=T380

redis.clients.jedis.Jedis@131303fredis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

redis.clients.jedis.Jedis@602b6bredis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

T204!=T787

T474!=T763

T163!=T542

T552!=T60

T604!=T820

T733!=T624

redis.clients.jedis.Jedis@131303fredis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

redis.clients.jedis.Jedis@d56b37redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

redis.clients.jedis.Jedis@151b0a5redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

redis.clients.jedis.Jedis@1295fe8redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

T784!=T948

T440!=T672

T97!=T867

……

以上结果出现许多键值不对应的情况。

 

解决方案

 

当Jedis读超时时,把此实例销毁,以免造成后续伤害。

销毁异常Jedis有三种方法:

方法1:加入红色代码,当读取Redis数据时任何异常都抛弃此Jedis实例

try{
jedis=pool.getResource();
result = jedis.get("T"+i);

if(StringUtils.isNotEmpty(result) && !result.equals("T"+i)){
System.out.println(result+"!=T"+i);
}

}catch(Exception e){
System.out.println(jedis+e.toString());
if(jedis!=null){
pool.returnBrokenResource(jedis);
}
}finally{
if(jedis!=null){
pool.returnResource(jedis);
}
}

方法2:配置JedisPool的TestOnBorrow为true

config.setTestOnBorrow(true);

 

方法3:配置JedisPool的TestOnReturn为true

config.setTestOnReturn(true);

 

总结

其实以上三种方法原理都是一样,就是检查Jedis的有效性,销毁异常Jedis链接实例。只是检查的时间不一样。

而导致量应用集群中其中之一出现,可以定位为有问题集群到Redis集群服务器的网速比正常集群的差(500ms超时限制)

 

方法1是在发生时检验销毁;

 

方法2是在从连接池获取Jedis实例时检查;

截图源码来自package org.apache.commons.pool.impl.GenericObjectPool

 

方法3是在归还Jedis实例给连接池时检查;

截图源码来自package org.apache.commons.pool.impl.GenericObjectPool

 

以上三种检查连接有效性方法都是一致:

boolean isNormal = false;

try{

                   isNormal = (jedis.isConnected()) && (jedis.ping().equals("PONG"));

}catch(Exception e){

                   isNormal = false;

}

 

注1,三种方法可以同时使用,但需要在检查性能消耗和功能稳定性之间衡量。


本文链接: JedisPool异常Jedis链接处理,转载请注明。

相关 [jedispool 异常 jedis] 推荐:

JedisPool异常Jedis链接处理 - wc的一些事一些情

- - 博客园_首页
基于JedisPool管理Jedis对象,通过get方法获取值,出现key对应的value值错误,例如:. 通过获取key为a的值,但获取了值b来. 同一套代码的项目,分别部署在两个不同的应用集群,其中一个集群出现这种问题,而另一个集群却没有出现. 通过表象可以看出,应该是链接池的Jedis对象链接出现错乱而导致的.

Redis客户端之Spring整合Jedis

- - 开源软件 - ITeye博客
1.下载相关jar包,并引入工程:. 2.将以下XML配置引入spring. 3.将shardedJedisPool注入相关的类中即可使用. * 设置一个key的过期时间(单位:秒). * @param key key值. * @param seconds 多少秒后过期. * @return 1:设置了过期时间 0:没有设置过期时间/不能设置过期时间.

使用Jedis的ShardedJedis做Redis集群

- - 丕子
之前一直没仔细看过ShardedJedis的代码,最近遇到了shard后集群扩容后的 数据迁移问题. 今天在看ShardedJedis源码的时候,发现ShardedJedis并没有使用节点的Ip和port做hash,而是用的instance的顺序或者name,太赞了. 一开始你可以设置足够多的instance,数据扩容的时候,只需要将几个instance的数据copy到别的机器上.

Redis客户端Jedis使用示例

- - 移动开发 - ITeye博客
Jedis 是 Redis 官方首选的 Java 客户端开发包. 工作过程总结的一个示例,贴出来,如下:.      * 在不同的线程中使用相同的Jedis实例会发生奇怪的错误. 但是创建太多的实现也不好因为这意味着会建立很多sokcet连接, .      * 也会导致奇怪的错误发生. 单一Jedis实例不是线程安全的.

Jedis的Publish/Subscribe功能的运用

- - 开源软件 - ITeye博客
转自: http://kingxss.iteye.com/blog/1420264. 一、Redis服务器端的安装和客户端Jedis的安装.    下载地址: http://redis.googlecode.com/files/redis-2.4.8.tar.gz. 在linux下运行如下命令进行安装.

[FAQ] Jedis使用过程中踩过的那些坑

- - 互联网 - ITeye博客
1. 一个 大坑:若实例化 JedisShardInfo 时不设置节点名称(name属性),那么当Redis节点列表的顺序发生变化时,会发生“ 键 rehash 现象”. 使用BTrace追踪redis.clients.util.Sharded的实时状态,验证“Jedis分片机制的一致性哈希算法”实现;.

基于jedis、redis-sentinel的redis主从、高可用、sharding架构

- - 开源软件 - ITeye博客
最近项目上需要对Redis(目前redis用的是2.8版本)做高可用、集群等优化,就扩展了jedis客户端(MasterSlaveJedis、MasterSlaveJedisPool、ShardedMasterSlaveJedis、ShardedMasterSlaveJedisPool),以满足以下几个需求:.

Jedis下的ShardedJedis(分布式)使用方法(一)

- - 互联网 - ITeye博客
原来项目中有用到Redis用作缓存服务,刚开始时只用一台Redis就能够满足服务,随着项目的慢慢进行,发现一台满足不了现有的项目需求,因为Redis操作都是原子性这样的特性,造成有时同时读写缓存造成查询效率的下降. 但是由于我们现在用的还是2.X版本,还是没有集群功能的(Redis作者在3.0版本中已经加入了集群功能), 因此只能使用2.x版本中自带的一个叫做ShardedJedis的来实现分布式缓存.

在Redis Sentinel环境下,jedis该如何配置 - iVictor - 博客园

- -
在Redis主从复制架构中,如果master出现了故障,则需要人工将slave提升为master,同时,通知应用侧更新master的地址. 这样方式比较低效,对应用侧影响较大. 为了解决这个问题,Redis 2.8中推出了自己的高可用方案Redis Sentinel. Redis Sentinel架构图如下:.

Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式等)介绍

- - 数据库 - ITeye博客
        redis是一个著名的key-value存储系统,而作为其官方推荐的java版客户端jedis也非常强大和稳定,支持事务、管道及有jedis自身实现的分布式.         在这里对jedis关于事务、管道和分布式的调用方式做一个简单的介绍和对比:.         最简单和基础的调用方式.