JedisPool异常Jedis链接处理
问题现象(jedis-2.1.0.jar)
基于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链接发现异常(如连接超时),不对异常连接销毁的话,会有数据缓存问题。
异常流程:
重现问题测试代码(设置1ms的readTimeOut时间,以便问题重现):
package test; import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Random;
import org.apache.commons.lang.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig; public class RedisTest implements Runnable{ public static JedisPool pool = null; static {
try {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxActive(100);
config.setMaxIdle(10);
config.setMaxWait(1000);
config.setTestOnBorrow(false);
config.setTestOnReturn(false);
config.setTestWhileIdle(true);
config.setTimeBetweenEvictionRunsMillis(30000);
config.setNumTestsPerEvictionRun(10);
config.setMinEvictableIdleTimeMillis(60000);
pool = new JedisPool(config, "192.168.22.213", 6379,1);
} catch (Exception e) {
System.out.println("【jedispool init error】");
}
} public void run() { Jedis jedis = null;
String result = "";
int i = new Random().nextInt(1000); 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()); }finally{
if(jedis!=null){
pool.returnResource(jedis);
}
} } /**
* 模拟2000线程并发
*/
public static void main(String[] args) throws Exception { for(int i=0;i<2000;i++){
new Thread(new RedisTest()).start();
}
}
}
执行结果:
……
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,三种方法可以同时使用,但需要在检查性能消耗和功能稳定性之间衡量。
文章更新时间:2016-07-01
更新内容:当我把jedis更新到2.8.1的时候使用returnBrokenResource和returnResource显示方法过期,原因是Jedis类重写了这个close方法,原本是没有的,它在close已经帮你判断好,源码如下所示:
public void close()
{
if (this.dataSource != null) {
if (this.client.isBroken())
this.dataSource.returnBrokenResource(this);
else
this.dataSource.returnResource(this);
}
else
this.client.close();
}
JedisPool异常Jedis链接处理的更多相关文章
- 解决Jedis链接报超时异常和connection reset异常的方法
一.链接池配置 <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig" ...
- 使用jedisPool管理jedis,使用jedis操作redis
ps:jedis是redis在java中的客户端操作工具 package com.test; 2 3 import java.util.HashMap; 4 import java.util.Iter ...
- jedis 链接池使用(转)
Jedis作为redis的最佳客户端,它提供了连接池的特性,“连接池”在通常情况下可以有效的提高应用的通信能力,并且这是一种良好的设计模式.Jedis的连接池设计基于apache commons-po ...
- [redis] redis 存取键值对常用的三种使用方式 - Jedis、JedisPool、Jedis分布式
|-Jedis 普通方式 |-JedisPool 连接池方式(需要引入pool相关jar) |-Jedis 分布式 (需要引入pool相关jar) 引入jedis2.7.0和commons.pool2 ...
- TCP中异常关闭链接的意义 异常关闭的情况
终止一个连接的正常方式是发送FIN. 在发送缓冲区中 所有排队数据都已发送之后才发送FIN,正常情况下没有任何数据丢失. 但我们有时也有可能发送一个RST报文段而不是F IN来中途关闭一个连接.这称为 ...
- 使用Java(Jedis)链接redis报java.net.ConnectException: Connection refused: connect的错误
redis环境:centos6 java代码运行环境:windows 第一种情况:未开启redis服务. redis-server /myredis/redis.conf (写你的redis配置文件的 ...
- redis集群搭建及java(jedis)链接
1.创建一个redis-cluster 目录 mkdir -p /usr/local/redis-cluster 创建6台redis服务器(单机下学习) mkdir 7001.mkdir 7002.m ...
- 项目里面加入redis单机版 和集群版的配置
第一步: 如果你是maven项目,你直接配置就可以了,如果不是需要下载这个包 jedis包 <!-- Redis 客户端 --> <dependency> ...
- (二)Redis之Jedis概念和HelloWorld实现以及JedisPool的使用
一.Jedis概念 实际开发中,我们需要用Redis的连接工具连接Redis然后操作Redis, 对于主流语言,Redis都提供了对应的客户端: 官网:https://redis.io/clients ...
随机推荐
- flask-admin章节二:wtforms的使用以及在数据库场景中使用QuerySelectField代替SelectField
概述 flask admin可以支持自定义视图,对于涉及到比较复杂的视图可以选择继承flask_admin.BaseView来定义自己期待的结构. 自定义的视图的每个函数可以使用flask_admin ...
- 远程debug hadoop
- MongoDB基本管理命令
MongoDB是一个NoSQL数据库系统:一个数据库可以包含多个集合(Collection),每个集合对应于关系数据库中的表:而每个集合中 可以存储一组由列标识的记录,列是可以自由定义的,非常灵活,由 ...
- Java自定义表单、自定义字段
最近想实现用户自定义数据库中的字段,我想大部分人第一想到的就是EAV(Entity-Attribute-Value),这种方式对于写一个小的毕业设计应该还可以使用,当然也有很多CMS系统采用这种方式, ...
- js模块和级联
1.模块 模块模式的一般形式是:一个定义了私有变量和函数的函数,利用闭包创建可以访问私有变量和函数的特权函数,最后返回这个特权函数,或者把它们保存到一个可访问的地方.使用模块模式就可以摒弃全局变量的使 ...
- 面向对象的OOA、OOD、OOP
OOA Object-Oriented Analysis:面向对象分析方法 是在一个系统的开发过程中进行了系统业务调查以后,按照面向对象的思想来分析问题.OOA与结构化分析有较大的区别.OOA所强调的 ...
- OD使用教程12
载入程序输入关键字: 双击进入程序 仔细看发现并没有跳转直接跳到这个mov,往上看发现retn上面有一个push,在这种编写手法当中这种组合相当于一个jmp, 跳到离它最近的一个值(在这就是004A5 ...
- sqlserver开启'xp_cmdshell'命令
--sql server中开启xp_cmdshell命令 1. --允许配置高级选项 GO RECONFIGURE GO . --开启xp_cmdshell服务 RECONFIGURE GO . -- ...
- 健忘vs总结
上周入职新公司,报道之前自己也曾想过要从头开始,用一个新的精神面貌来迎接新的起点,培养一些新的习惯. 周四是15日,新公司的发薪日(当然还没有我的份~),小组群内一个刚毕业的新人兴冲冲的说终于领到第一 ...
- 20151012 C# 第一篇 字符与字符串
20151012 字符与字符串: Char.String等类来表示 字符类Char 1. 字符类Char 表示一个 Unicode 字符,(Unicode字符是计算机通用的字符编码,对不同语言中的每个 ...