错误信息:

IllegalStateException: Invalidated object not currently part of this pool

一、问题描述

前些天用多线程执行操作测试验证vanyar-redis连接池,应用是刚重启的状态,执行操作是,开启10个线程同时执行10000次操作。

如下:

执行操作完毕后发现控制台输出9个下面错误信息:

该错误大致意思是说:不能将redis连接放回池内,放回连接池的对象是无效的对象。在网上查了很多同类错误,都说是进行了两次returnResource释放连接资源造成的,因为第一次return成功以后,第二次return就会报上面这个错误。但是显然,我翻遍了代码并没有两次调用returnResource。

查看redis服务端的连接数详细信息如下,前9个连接,idle=453,空闲了453秒了,依然没有释放,而连接池设置的是空闲60秒就会被释放,明显发生异常了。

初步怀疑是多线程执行redis操作,初始化redis连接池有问题。于是重启应用,先执行单线程redis操作,再执行多线程redis操作,没有发生上面的问题。redis服务端连接均能正常释放。由此得出结论,当线程池在未初始化的时候,由于多线程同时执行redis连接池初始化工作引起的问题。

看代码(RedisJedisPool未优化之前):当10个线程同时请求redis连接资源时,10个线程都发现连接池为空(因为创建连接池相比创建线程比较耗时),这时10个线程都各自初始化成功一个连接池,并从中取得redis连接,并执行了redis操作。执行完毕,returnResource的时候,由于此时pool变量的引用是最后一个线程初始化的连接池,前面9个线程获得的redis连接并不属于最后一个连接池的资源,所以抛错:IllegalStateException: Invalidated object not currently part of this pool

二、报错原因分析

线程1 : 创建redis连接池1 : 获得redis连接1

线程2 : 创建redis连接池2 : 获得redis连接2

线程3 : 创建redis连接池3 : 获得redis连接3

……

线程8 : 创建redis连接池8 : 获得redis连接8

线程9 : 创建redis连接池9 : 获得redis连接9

线程10 : 创建redis连接池10 : 获得redis连接10

全局变量pool引用 指向 redis连接池10

当线程1-9 把redis连接1-9 归还给pool-redis连接池10

reds连接池10自然就报错,说:

IllegalStateException: Invalidated object not currently part of this pool

三、解决办法

由于创建线程池,连接池等工作都是相对比较耗时的,所以我们一般放在应用启动的时候就初始化,把连接池的初始化工作交给Spring容器管理,同时把初始化连接池和获取连接两个操作实现方法分离,对初始化连接池的方法加上同步锁机制,并且二次判断是否为空,就算多线程情况下,在二次判断是否为空的时候,pool已经不为空了,直接返回。现在多线程安全的问题就得以解决。

附上,解决前后对比图:

补充知识:java spring框架中方法级redis的连接自动获取和释放实现

java中使用redis总是需要处理redis连接的获取,释放等操作,每次使用都会使代码变的特别丑陋,模仿spring中aop的实现,用动态代理写一个 连接自动获取和释放的工具

主要思路

JedisManageSupport 抽象类 类似于 aop的切入点,所有继承了该类(一般都是service层)的类,可以使用提供的获取redis的方法获取redis,并且不需要释放

JedisBeanPostProcessor 继承BeanPostProcessor ,会在bean初始化时执行自己定义的逻辑:

如果A类继承了 JedisManageSupport ,就会获取redis连接并且放到JedisManageSupport 的成员变量里,A类的实例(其实是cglib动态代理生成的

A类的子类的实例)就可以使用该redis连接 进行相关操作了

代理类的实例见源码

源码如下

public class JedisBeanPostProcessor implements BeanPostProcessor {

@Autowired

ShardedJedisPool shardedJedisPool;

static final Logger logger = Logger.getLogger(JedisBeanPostProcessor.class);

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

if (bean instanceof JedisManageSupport) {

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(bean.getClass());

enhancer.setCallback(new JedisInterceptor(shardedJedisPool, bean));

Object targetBean = enhancer.create();

return targetBean;

}

else {

return bean;

}

}

@Override

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

return bean;

}

}

class JedisInterceptor implements MethodInterceptor {

static final Logger logger = Logger.getLogger(JedisInterceptor.class);

ShardedJedisPool pool;

Object src;

public JedisInterceptor(ShardedJedisPool pool, Object src) {

this.pool = pool;

this.src = src;

}

@Override

public Object intercept(Object target, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {

Object result = null;

if (target instanceof JedisManageSupport) {

if (this.isDeclaredMethod(target, method)) {

ShardedJedis jedis = null;

try {

JedisManageSupport support = (JedisManageSupport) src;

jedis = pool.getResource();

support.setShardedJedis(jedis);

// logger.debug("调用之前注入jedis对象,method:" + method);

/**

* 下面代码可以使用 method.invoke(src,arguments)。 不能使用

* methodProxy.invokeSuper(target,arguments);

* 因为A类中用Autowired注入的属性,生成代理的子类B后,因为子类B是新建的类。从父类继承的属性没有被初始化,

* 使用methodProxy.invokeSuper()执行是,会报空指针异常.

*/

result = methodProxy.invoke(src, arguments);

support.setShardedJedis(null);

}

catch (Exception e) {

pool.returnBrokenResource(jedis);

e.printStackTrace();

}

finally {

if (jedis != null) {

pool.returnResource(jedis);

}

// logger.debug("调用之后归还jedis对象,method:" + method);

}

}

else {

result = methodProxy.invoke(src, arguments);

}

}

else {

throw new Exception("使用该代理必须继承JedisManageSupport");

}

return result;

}

/**

* 是否是target类本身定义的非私有的方法,还是继承的父类

* @return true是target自己类的并且不是私有的的,

*/

private boolean isDeclaredMethod(Object target, Method arg1) {

Method temp = null;

try {

temp = target.getClass().getDeclaredMethod(arg1.getName(), arg1.getParameterTypes());

}

catch (SecurityException e) {

e.printStackTrace();

}

catch (NoSuchMethodException e) {

e.printStackTrace();

}

/**

* 不为null,并且是非私有的,返回true

*/

if (temp != null) {

return true;

}

else {

return false;

}

}

}

public abstract class JedisManageSupport {

ThreadLocal jedisHolder = new ThreadLocal();

public final ShardedJedis getShardedJedis() {

return jedisHolder.get();

}

public final void setShardedJedis(ShardedJedis jedis) {

jedisHolder.set(jedis);

}

/**

* 如果某个键不同单位之间也不会重复,可以使用这个方法生成redis的键

*/

public final byte[] assemKey(String baseKey) {

Assert.isTrue(StringUtils.isNotBlank(baseKey), "参数不能为空");

return baseKey.getBytes();

}

/**

* 根据tableName+prefix 构造唯一key与assemKey(String baseKey, String tableName)

* 规则一致

*/

public final byte[] assemKeyByPrefix(String tableName, String baseKey) {

Assert.isTrue(StringUtils.isNotBlank(baseKey), "参数不能为空");

Assert.isTrue(StringUtils.isNotBlank(tableName), "参数不能为空");

UnitInfo unit = WebService.getUnitInfo();

Assert.isTrue(unit != null, "单位信息获取不到");

return (tableName + "-" + unit.getPrefix() + "-" + baseKey).getBytes();

}

/**

*

* 不同前缀的表中可能有相同的键,同一个表中也可能是有重复的baseKey时,用这个生成redis的key 比如 用户信息表的

* username字段,不同的用户信息表允许重复的username,mooc_t_userinfo

* 也允许有相同的账号,所以生成redis的key时,需要用到单位的mooc_school 放入redis中

*/

public final byte[] assemKeyByFid(String tableName, String baseKey) {

UnitInfo unit = WebService.getUnitInfo();

Assert.isTrue(unit != null, "单位信息获取不到");

return (tableName + "-" + unit.getMoocSchool() + "-" + baseKey).getBytes();

}

}

以上这篇解决Redis连接无法正常释放的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

a频繁连接不上redis_解决Redis连接无法正常释放的问题相关推荐

  1. navacate连接不上mysql_解决navicat连接不上mysql服务器

    设置mysql密码 如果mysql用户密码为空,需要设置后第三方mysql操作工具才可以连接 进入数据库:mysql -uroot -p use mysql; update user set pass ...

  2. mysql创建连接失败怎么办,MYSQL在Windows 2003上连接不上的解决方法

    MYSQL在Windows 2003上连接不上的解决方法 今天搭建mysql数据库,连接了半天,都提示10061错误,以为密码出了问题试了半天,压根连服务都连接不上,忙活了半个多小时,才发现是系统补丁 ...

  3. 问道打开时显示连接服务器失败怎么办,荒野行动连接不上服务器怎么办 服务器连接失败解决-游侠手游...

    在荒野行动中想要进入游戏,连接服务器便是第一个步骤.但是很多的玩家却出现了无法连接服务器的情况.所以这里小编就为大家带来了无法连接服务器的解决办法,还在为这个问题而困惑的玩家不妨随小编的脚步一起来看看 ...

  4. 荒野行动显示未选择服务器咋弄,荒野行动连接不上服务器怎么办 荒野行动连接不上服务器解决方法...

    荒野行动连接不上服务器解决方法: 网络问题 如果是连接不上服务器的话,那么最应该考虑的就是网络的问题了,一旦出现了网络问题,那么在玩的时候,就会显示进不去游戏,玩家们可以选择wifi进行运行游戏,这样 ...

  5. xshell突然连接不上虚拟机解决办法

    xshell突然连接不上虚拟机解决办法, 重启网络,三个命令: systemctl stop NetworkManager systemctl disable NetworkManager syste ...

  6. Ubuntu14.04 搜索不到WIFI或连接不上的解决方法。

    Ubuntu14.04 搜索不到WIFI或连接不上的解决方法. 参考文章: (1)Ubuntu14.04 搜索不到WIFI或连接不上的解决方法. (2)https://www.cnblogs.com/ ...

  7. 腾讯云主机SSH连接不上如何解决

    腾讯云主机SSH连接不上如何解决 腾讯云主机如果安装的是宝塔面板系统,你的防火墙已经被宝塔接管了,就算登录腾讯云控制台,创建密钥,并且绑定密钥,也是链接不上的.必须登录宝塔面板 1. 2. 快去登录你 ...

  8. 记录阿里云虚拟主机FTP连接不上的解决办法

    记录阿里云虚拟主机FTP连接不上的解决办法 FTP工具:filezilla 默认设置: 这样是连接不上的 解决办法: 这样就好了 成功截图:

  9. 计算机网怎样连接网络连接不上,网络连接不上,详细教您电脑网络连接不上怎么解决...

    我们的生活因为无线局域网的出现获得了很大的方便,不过因为无限网络信道特有的性质,有时我们会在使用过程中出现电脑网络连接不上的情况,这使得无线网络连接具有不稳定性,影响了服务质量.应该怎么办呢?下面,小 ...

最新文章

  1. R语言使用caret包构建遗传算法树模型(Tree Models from Genetic Algorithms )构建回归模型、通过method参数指定算法名称
  2. oracle 唯一索引,唯一约束,主键之间的联系
  3. mysql首次安装后原始密码存放位置
  4. 剑指offer之二叉树的下一个结点
  5. mysql从入门到精通之数据库基本概念理解
  6. 优雅的使用Js或CSS处理文本的截断与展示
  7. tomcat性能调优和性能监控(visualvm)
  8. 关于单点登录的简单原理和实现步骤
  9. 在Linux平台使用Firefox 登陆EBS r12环境
  10. 写作之: 算法流程图latex代码和范例
  11. DRAM Timing
  12. WEB-QTP随想录—李密的猜想
  13. vin端口是什么意思_这些问题你都答不上,还好意思说自己学过网络?
  14. [译]无迹卡尔曼滤波教程
  15. 解决无法打开B站网页问题
  16. PAT | 1025 反转链表 (25分)【超时问题 + 柳神代码】
  17. MATLAB2014a,MEX编译问题
  18. 计算机神书『编码:隐匿在计算机软硬件背后的语言』
  19. 用文本框输入来查处键盘的Uintin的值
  20. RK1126平台项目总结

热门文章

  1. dev-c++开发的全鼠标操作控制台战棋
  2. Leetcode1038. 把二叉搜索树转换为累加树
  3. Javafx+MySQL 学生成绩管理系统
  4. linux挂载4tb硬盘分区,centos7挂载新加4T硬盘到home
  5. iOS三句话实现文本转语音:AVSpeechSynthesizer
  6. 医疗信息化与医院评审
  7. 做了个多语种网站 不能被GOOGLE,yahoo收录,baidu可以收录 终于找到问题
  8. 玩转百度即用API(5)——空气质量指数查询
  9. 第41部分-Linux x86 64位汇编MMX使用
  10. quartz集群模式下qrtz_triggers表trigger_state变ERROR分析