问题现象
基于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链接发现异常(如连接超时),不对异常连接销毁的话,会有数据缓存问题。
异常流程:
重现问题测试代码:
执行结果:
……
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实例
方法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链接处理,转载请注明。