这一天风和日丽,我很荣幸的参加进入组织的活动,这个组织依然是一群闷骚的少年,热火朝天的甩着膀子,写着神圣的 Java 代码,偌大的办公室,只能听见噼里啪啦的敲击键盘声!

好骚气的组织!!!

------------------------------------------------------------------------------------------------------------------------

进入组织后,给我随手就撩过来一个 git 地址,习惯性的通过 git clone <git-url> ,将代码 dang  下来!

我的 IDEA 已经饥渴难耐,说时迟那时快。鼠标飞快的飞到桌面的  , 以盲狙的速度进行了双击!经过 10s 的长达等待。主界面终于展示在我的眼前。仿佛看到了我梦中的女神——怦然心动,无法形容我的处境。

作为一个不会技术的二溜子,岂能就此作罢,扫视一眼后发现,原来是仰慕已久的 Web 项目基佬(这么说主要是因为培训机构 3 个月能生产一批,而且都是各行各业的男性同胞),瞬间心情大落!吾等倒要看看你是什么妖孽,没有妖孽也要给你制造一批出来。

拉出我的汤姆猫( tomcat ), 将它迅速加载进来。紧接着一个飘逸的  “Shift + F10” (IDEA 的 快捷键)闪过。

一个 http://localhost:8080 的界面自动打开在我的眼前。

哎呀,好帅气的界面……

输入测试账号、测试密码、登录验证码……一波骚操作之后,功能都可以正常使用!

待我休息三秒后,挠了挠头,对 组织成员 A 说:嗨,帅哥,发 50 个请求过来玩玩!

哈……果然,出现了骚气的问题,页面请求处于 pending 状态,过 10s 报 timeout , 后台日志报错:

org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisException: Could not get a resource from the poolat org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:204)at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:348)at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:129)at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:92)at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:79)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:194)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:169)at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:91)at org.springframework.data.redis.core.DefaultValueOperations.increment(DefaultValueOperations.java:63)

注:说明一下,我们得 redis 环境配置主要为:

<property name="maxIdle" value="50"/>

<property name="minIdle" value="20"/>

<property name="usePool" value="true" />

----------------------------------------------------------------------------------------------------------------------------------

看到如此之问题,岂不是很蛋疼,立即跳起来,左手叉腰,右手指向天花板。大喊一声:拿我的 5 米长刀来。

我跟着异常中提示的异常堆栈信息,我打开代码,并定位到异常的行数位置,查看代码。大多都是通过  redisTemplate 来与 Redis 交互。redis 的连接池是通过 common-pools 来管理的,redisTemplate 之前我在其他项目也使用过,不应该会出现泄漏的问题。

怀着激动不安的心情,我进去到如下代码中进行了代码跟踪:

ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();

这里我以 valueOperations.set()进行了代码跟踪, set 方法的实现如下:

public void set(K key, V value) {final byte[] rawValue = rawValue(value);execute(new ValueDeserializingRedisCallback(key) {protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {connection.set(rawKey, rawValue);return null;}}, true);}

追踪代码,set 方法调用内部,我很能确定的是,调用后,链接进行了关闭操作(代码如下)!其实,严格来说,使用连接池,通过 borrowObject()方法获取的,最终当然是通过 returnObject()! 读者有兴趣可以直接了解 : common-pools2.jar 源代码了解。

/*** Executes the given action object within a connection that can be exposed or not. Additionally, the connection can* be pipelined. Note the results of the pipeline are discarded (making it suitable for write-only scenarios).* * @param <T> return type* @param action callback object to execute* @param exposeConnection whether to enforce exposure of the native Redis Connection to callback code* @param pipeline whether to pipeline or not the connection for the execution* @return object returned by the action*/public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");Assert.notNull(action, "Callback object must not be null");RedisConnectionFactory factory = getConnectionFactory();RedisConnection conn = null;try {if (enableTransactionSupport) {// only bind resources in case of potential transaction synchronizationconn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);} else {conn = RedisConnectionUtils.getConnection(factory);}boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);RedisConnection connToUse = preProcessConnection(conn, existingConnection);boolean pipelineStatus = connToUse.isPipelined();if (pipeline && !pipelineStatus) {connToUse.openPipeline();}RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));T result = action.doInRedis(connToExpose);// close pipelineif (pipeline && !pipelineStatus) {connToUse.closePipeline();}// TODO: any other connection processing?return postProcessResult(result, connToUse, existingConnection);} finally {RedisConnectionUtils.releaseConnection(conn, factory);}}

看了看我的 5 米长刀,再看看这个异常,虎躯一震:难不成真要让我的大刀上场?!

干!!!

天下大事,要干成功,一般需要三步:

1. 千军易找,一将难求!(打开终端,找到程序的 pid )

2. 招兵买马屯田生产(获取应用堆栈信息,命令为: jmap -dump,format=b,file=/home/hadoop/my-dump.hprof <pid> )

3. 拿下城池,弑帝称王!(使用 Mat 分析定位、解决问题)

注: MAT 全称为:Eclipse Memory Analyzer, 下载地址为:http://www.eclipse.org/mat/ , 读者下载完后,可以考虑修改一下  MemoryAnalyzer.ini 文件中 -Xmx 的大小( 如果你的 hprof 很大的话,会造成 OOM,导致无法继续分析 )

--------------------------------------------------------------------------------------------------------------------------

我的文件打开后,如下图,占用内存并不大。我们主要是分析链接泄漏。既然是泄漏,肯定就存在有内存不能释放,并且可 DUMP 。

点击内存占用最高的饼图位置,会出现如下图示的菜单可选择。选择 "show objects by class " ->  “ by incoming references ” !

随后会打开  "class  references" 窗口, 果断的在  ClassName 顶部的搜索框中输入 “com” ( 我们只关注我们关注的内容),由于是连接池泄漏,所以其他的内容我们可以不用理会了哈,直接看 org.apache.commons.pool2.impl.GenericObjectPool  即可。

在该类上面右击,选择  “Java Basics” ——> "Open In Dominator Tree" ,打开 !

一步步的展开  org.apache.commons.pool2.impl.GenericObjectPool 我们可以看到如下图。哈,,,大大的   hscan !

到这一步,我们已经很清晰了。 在 IDEA 中搜索  hscan 相关的代码。果然,找到了一些使用 scan 命令的地方,再细细端详才发现, 罪魁祸首为:

Cursor<Map.Entry<String, Bean>> cursor = hashOperations.scan(scanOption);

cursor 使用完毕后,没有看到调用  .close() 的地方。

果断加上,再测一把!!!顺利通过。

结论:

在平时的代码中,一定要记得在使用链接、游标、流等位置,记得关闭!否则会造成不可预料的问题。

问题顺利解决,收起我的 5 米大刀!

端起我的碧螺春,轻抿一口!

窗外不知何时漂起了小雨!

转载于:https://my.oschina.net/Rayn/blog/2032408

记一次 Redis 连接池泄漏问题排查相关推荐

  1. python redis连接池获取后关闭_python通过连接池连接redis,操作redis队列

    在每次使用redis都进行连接的话会拉低redis的效率,都知道redis是基于内存的数据库,效率贼高,所以每次进行连接比真正使用消耗的资源和时间还多.所以为了节省资源,减少多次连接损耗,连接池的作用 ...

  2. Java的Redis连接池代码性能不错

    其实这个是引用自网友http://blog.csdn.net/tuposky/article/details/45340183,有2个版本,差别就是ReentrantLock和synchronized ...

  3. redis连接池操作

    /** * @类描述 redis 工具 * @功能名 POJO * @author zxf * @date 2014年11月25日 */ public final class RedisUtil { ...

  4. java操作redis redis连接池

    redis作为缓存型数据库,越来越受到大家的欢迎,这里简单介绍一下java如何操作redis. 1.java连接redis java通过需要jedis的jar包获取Jedis连接. jedis-2.8 ...

  5. redis专题:redis键值设计、性能优化以及redis连接池配置

    文章目录 1.redis键值设计 ①:key设计规范 ②:value设计规范 2. 命令使用优化 3. redis连接池配置参数设计 4. redis连接池预热 5. redis的key过期删除策略 ...

  6. Java Redis 连接池 Jedis 工具类,java基础面试笔试题

    我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码或搜索下图红色VX号,加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起 ...

  7. Java的Redis连接池代码

    2019独角兽企业重金招聘Python工程师标准>>> 其实这个是引用自网友http://blog.csdn.net/tuposky/article/details/45340183 ...

  8. php redis 集群 长连接池,php如何实现redis连接池

    项目使用的是php,生产环境使用的是redis集群,连接的地址是配置的域名,每次创建连接必须要经过一次域名解析,频繁的创建链接效率低下且经常出现超时的情况,有没有在生产环境实现redis链接池的,分享 ...

  9. SpringBoot 配置 Redis 连接池

    前言 SpringBoot2.0默认采用 Lettuce 客户端来连接 Redis 服务 默认是不使用连接池的,只有配置 redis.lettuce.pool下的属性的时候才可以使用到redis连接池 ...

最新文章

  1. Java线程:线程的调度-合并
  2. 如何获取 Process.Start 打开进程的输出结果?
  3. [Err] 1231 - Variable 'sql_mode' can't be set to the value of 'NULL
  4. scrapy自定义Request的缓存策略(减少内存占用)
  5. Keras-Sequential模型(1)
  6. vc mysql query_我要使用mysql_query()这个函数,在VC中需要做什么工作?
  7. 同义句转换在线翻译器的软件
  8. 大学统计学基础知识笔记
  9. axios 最详细封装
  10. 浅谈计算机网络安全问题和对策
  11. 计算机本地连接无internet访问权限,ipv4连接无internet访问权限怎么解决
  12. iphone/ipad保存图片问题
  13. Unity制作UI翻页动画
  14. linux正在等待声音系统响应,linux声音系统较好的解决方案alsa+esd
  15. usb无线网卡和U盘同时使用
  16. 机械键盘各种轴的特点
  17. Linux 文件与目录的管理
  18. RAAVPPSPSLSRHSSPHQSEDEEE
  19. java入门考点_java入门基础知识点总结
  20. python习题练习

热门文章

  1. input file图片上传(使用OSS Javscrtipt 上传到服务器)以及图片裁剪(cropper.js)
  2. sed在shell脚本中引用变量
  3. php短信炸弹,php发送短信炸弹
  4. 辉煌一时的金立如今却沦为山寨机?只因做错了这一点
  5. LaTeX环境安装及入门之入门使用
  6. Acwing---1231.航班时间
  7. 访问者模式-好人打贱人
  8. eclipse配置opencv和javacv环境
  9. ERROR:Session/line number was not unique in database. History logging moved to new session.
  10. 单目标应用:基于北方苍鹰优化算法NGO的概率神经网络PNN数据分类(提供MATLAB代码)