点击上方“朱小厮的博客”,选择“设为星标”后台回复"书",获取后台回复“k8s”,可领取k8s资料

首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象

刚开始当测试抱怨环境响应慢的时候 ,我们重启一下应用,应用恢复正常,于是没做处理。

但是后来问题出现频率越来越频繁,越来越多的同事开始抱怨,于是感觉代码可能有问题,开始排查。

首先发现开发的本地ide没有发现问题,应用卡死时候数据库,redis都正常,并且无特殊错误日志。开始怀疑是sandbox环境机器问题(测试环境本身就很脆!_!)

于是ssh上了服务器 执行以下命令

top

这时发现机器还算正常,于是打算看下jvm 堆栈信息

先看下问题应用比较耗资源的线程

执行 top -H -p 12798

找到前3个相对比较耗资源的线程

jstack 查看堆内存

jstack 12798 |grep 12799的16进制 31ff

没看出什么问题,上下10行也看看,于是执行

看到一些线程都是处于lock状态。但没有出现业务相关的代码,忽略了。这时候没有什么头绪。思考一番。决定放弃这次卡死状态的机器

为了保护事故现场 先 dump了问题进程所有堆内存,然后debug模式重启测试环境应用,打算问题再显时直接远程debug问题机器

第二天问题再现,于是通知运维nginx转发拿掉这台问题应用,自己远程debug tomcat。

自己随意找了一个接口,断点在接口入口地方,悲剧开始,什么也没有发生!API等待服务响应,没进断点。

这时候有点懵逼,冷静了一会,在入口之前的aop地方下了个断点,再debug一次,这次进了断点,f8 N次后发现在执行redis命令的时候卡主了。

继续跟,最后在到jedis的一个地方发现问题:

/*** Returns a Jedis instance to be used as a Redis connection. The instance can be newly created or retrieved from a* pool.* * @return Jedis instance ready for wrapping into a {@link RedisConnection}.*/
protected Jedis fetchJedisConnector() {try {if (usePool && pool != null) {return pool.getResource();}Jedis jedis = new Jedis(getShardInfo());// force initialization (see Jedis issue #82)jedis.connect();return jedis;} catch (Exception ex) {throw new RedisConnectionFailureException("Cannot get Jedis connection", ex);}
}

上面pool.getResource()后线程开始wait

public T getResource() {try {return internalPool.borrowObject();} catch (Exception e) {throw new JedisConnectionException("Could not get a resource from the pool", e);}
}

return internalPool.borrowObject(); 这个代码应该是一个租赁的代码,接着跟

public T borrowObject(long borrowMaxWaitMillis) throws Exception {this.assertOpen();AbandonedConfig ac = this.abandonedConfig;if (ac != null && ac.getRemoveAbandonedOnBorrow() && this.getNumIdle() < 2 && this.getNumActive() > this.getMaxTotal() - 3) {this.removeAbandoned(ac);}PooledObject p = null;boolean blockWhenExhausted = this.getBlockWhenExhausted();long waitTime = 0L;while(p == null) {boolean create = false;if (blockWhenExhausted) {p = (PooledObject)this.idleObjects.pollFirst();if (p == null) {create = true;p = this.create();}if (p == null) {if (borrowMaxWaitMillis < 0L) {p = (PooledObject)this.idleObjects.takeFirst();} else {waitTime = System.currentTimeMillis();p = (PooledObject)this.idleObjects.pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS);waitTime = System.currentTimeMillis() - waitTime;}}if (p == null) {throw new NoSuchElementException("Timeout waiting for idle object");}

其中有段代码

if (p == null) {if (borrowMaxWaitMillis < 0L) {p = (PooledObject)this.idleObjects.takeFirst();} else {waitTime = System.currentTimeMillis();p = (PooledObject)this.idleObjects.pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS);waitTime = System.currentTimeMillis() - waitTime;}
}

borrowMaxWaitMillis<0会一直执行,然后一直循环了 开始怀疑这个值没有配置

找到redis pool配置,发现确实没有配置MaxWaitMillis,配置后else代码也是一个Exception 并不能解决问题

继续F8

public E takeFirst() throws InterruptedException {this.lock.lock();Object var2;try {Object x;while((x = this.unlinkFirst()) == null) {this.notEmpty.await();}var2 = x;} finally {this.lock.unlock();}return var2;
}

到这边 发现lock字眼,开始怀疑所有请求api都被阻塞了

于是再次ssh 服务器 安装 arthas ,(Arthas 是Alibaba开源的Java诊断工具)

执行thread命令

发现大量http-nio的线程waiting状态,http-nio-8083-exec-这个线程其实就是出来http请求的tomcat线程

随意找一个线程查看堆内存

thread -428

这是能确认就是api一直转圈的问题,就是这个redis获取连接的代码导致的,

解读这段内存代码  所有线程都在等 @53e5504e这个对象释放锁。于是jstack 全局搜了一把53e5504e ,没有找到这个对象所在线程。

自此。问题原因能确定是 redis连接获取的问题。但是什么原因造成获取不到连接的还不能确定

再次执行 arthas 的thread -b (thread -b, 找出当前阻塞其他线程的线程)

没有结果。这边和想的不一样,应该是能找到一个阻塞线程的,于是看了下这个命令的文档,发现有下面的一句话

好吧,我们刚好是后者。。。。

再次整理下思路。这次修改redis pool 配置,将获取连接超时时间设置为2s,然后等问题再次复现时观察应用最后正常时干过什么。

添加一下配置

JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
.......
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxWaitMillis(2000);
.......
jedisConnectionFactory.afterPropertiesSet();

重启服务,等待。。。。

又过一天,再次复现

ssh 服务器,检查tomcat accesslog ,发现大量api 请求出现500,

org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource fr
om the poolat org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:140)at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:229)at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:57)at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:128)at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:91)at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:78)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:177)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:152)at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:85)at org.springframework.data.redis.core.DefaultHashOperations.get(DefaultHashOperations.java:48)

找到源头第一次出现500地方,

发现以下代码

.......
Cursor c = stringRedisTemplate.getConnectionFactory().getConnection().scan(options);
while (c.hasNext()) {
.....,,}

分析这个代码,stringRedisTemplate.getConnectionFactory().getConnection()获取pool中的redisConnection后,并没有后续操作

也就是说此时redis 连接池中的链接被租赁后并没有释放或者退还到链接池中,虽然业务已处理完毕 redisConnection 已经空闲,但是pool中的redisConnection的状态还没有回到idle状态

正常应为

自此问题已经找到。

总结:spring stringRedisTemplate 对redis常规操作做了一些封装,但还不支持像 Scan SetNx等命令,这时需要拿到jedis Connection进行一些特殊的Commands

使用

stringRedisTemplate.getConnectionFactory().getConnection()

是不被推荐的

我们可以使用

stringRedisTemplate.execute(new RedisCallback() {@Overridepublic Cursor doInRedis(RedisConnection connection) throws DataAccessException {return connection.scan(options);}});

来执行,或者使用完connection后 ,用

RedisConnectionUtils.releaseConnection(conn, factory);

来释放connection.

同时,redis中也不建议使用keys命令,redis pool的配置应该合理配上,否则出现问题无错误日志,无报错,定位相当困难。

来源:https://my.oschina.net/xiaomu0082/blog/2990388

想知道更多?描下面的二维码关注我

后台回复"技术",加入技术群

后台回复“k8s”,可领取k8s资料

【精彩推荐】

  • ClickHouse到底是什么?为什么如此牛逼!

  • 原来ElasticSearch还可以这么理解

  • 面试官:InnoDB中一棵B+树可以存放多少行数据?

  • 架构之道:分离业务逻辑和技术细节

  • 星巴克不使用两阶段提交

  • 面试官:Redis新版本开始引入多线程,谈谈你的看法?

  • 喜马拉雅自研网关架构演进过程

  • 收藏:存储知识全面总结

  • 微博千万级规模高性能高并发的网络架构设计

记一次因 Redis 使用不当导致应用卡死 bug 的排查及解决!相关推荐

  1. java redis使用卡死_记一次找因 redis 使用不当导致应用卡死 bug 的过程

    原标题:记一次找因 redis 使用不当导致应用卡死 bug 的过程 作者:小木 my.oschina.net/xiaomu0082/blog/2990388 首先说下问题现象:内网sandbox环境 ...

  2. 记一次找因 Redis 使用不当导致应用卡死 bug 的过程

    首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象 刚开始当测试抱怨环境响应慢的时候 ,我们重启一下应用,应用恢复正常,于是没做处理.但是后来问题出现频率越来越频 ...

  3. 不是keys,记一次因 redis 使用不当导致应用卡死 的过程

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 来源:腾讯技术工程 首先说下问题现象:内网sandbox环境API持续1周 ...

  4. 记一次找因Redis使用不当导致应用卡死bug的过程

    作者:小木 首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象 刚开始当测试抱怨环境响应慢的时候 ,我们重启一下应用,应用恢复正常,于是没做处理.但是后来问题出现 ...

  5. 记一次因Redis使用不当导致应用卡死过程

    作者:小木 首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象 刚开始当测试抱怨环境响应慢的时候 ,我们重启一下应用,应用恢复正常,于是没做处理.但是后来问题出现 ...

  6. Redis使用不当导致应用卡死

    作者:小木 来源:http://rrd.me/ezfTj 首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象 刚开始当测试抱怨环境响应慢的时候 ,我们重启一下应用 ...

  7. MemoryCache 使用不当导致的一个 BUG

    MemoryCache 使用不当导致的一个 BUG Intro 前几天发现代码里的一个 BUG,原因是 MemoryCache 使用不当,可以对于很多人来说可能都知道,但还是想分享记录一下,避免以后写 ...

  8. 记一次CentOS7因Redis配置不当导致被Root提权沦为矿机修复过程

    未曾想过,那些年影视剧中黑客们的精彩桥段,竟在2020这个充满魔幻的年份,变成了现实. 前几日傍晚突然收到了来自阿里云安全中心的提醒,服务器疑似受到攻击了.想不到我那用作学习的机器,有朝一日竟然沦为矿 ...

  9. 记一次自定义 Redis 分布式锁导致的故障

    点击上方关注 "终端研发部" 设为"星标",和你一起掌握更多数据库知识 背景 企微报警群里连续发出生产环境报错警告,报错核心信息如下: redis setNX ...

最新文章

  1. 自从上线了 Prometheus 监控告警,真香!
  2. ubuntu16.04开机卡死解决方案
  3. 7个jquery easy ui 基本组件图解
  4. 机器视觉 · 工业相机
  5. java的jbutton啥意思,Jbutton和Jpanel和JFrame之间的区别?
  6. Swift:用UICollectionView整一个瀑布流
  7. 修改 Powerpoint 的 Master slide
  8. linux进程通讯-纯文本文件
  9. 前端学习(3030):vue+element今日头条管理-顶部导航栏布局
  10. 二、搭建Apache服务器 模板引擎
  11. cakephp 1.3 Controller
  12. datagrid在MVC中的运用05-加入时间搜索条件,枚举填充下拉框
  13. neatupload上传文件配置
  14. vue3 + typescript + echarts后端动态获取数据显示问题总结
  15. linux怎样使用小米线刷工具,在linux上怎么样线刷小米手机
  16. FFmpeg —— 屏幕录像机
  17. 线性判别分析(Linear Discriminant Analysis)
  18. 关于赛马的问题,25匹赛出前3名或者前5名
  19. 精密划片机在钽酸锂晶圆切割案例
  20. 背课文记单词,读课文记单词,读文章记单词;40篇文章搞定3500词;71篇文章突破中考单词;15篇文章贯通四级词汇;15篇文章贯通六级词汇

热门文章

  1. JQUERY阻止点击事件冒泡
  2. 微信小程序 - 点击事件传参
  3. (一)零基础入门C语言 --- C语言之入门课程
  4. 20172325 2018-2019-2 《Java程序设计》第六周学习总结
  5. KDJB-802微机继电保护测试仪
  6. 一体式电动机综合保护期ALP300的保护功能说明 安科瑞 时丽花
  7. Powershell函数返回值问题
  8. php fsockopen 传参,Php应用fsockopen发送http请求
  9. 大数据从业者必读书籍《数据仓库工具箱》-第三章笔记
  10. layui打印自定义表格功能