同事乱用Redis卡爆,我真是醉了
来源:my。oschina。netxiaomu0082blog2990388
首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象
刚开始当测试抱怨环境响应慢的时候,我们重启一下应用,应用恢复正常,于是没做处理。但是后来问题出现频率越来越频繁,越来越多的同事开始抱怨,于是感觉代码可能有问题,开始排查。top命令
首先发现开发的本地ide没有发现问题,应用卡死时候数据库,redis都正常,并且无特殊错误日志。开始怀疑是sandbox环境机器问题(测试环境本身就很脆!!)
于是ssh上了服务器执行以下命令
top
图片
这时发现机器还算正常,于是打算看下jvm堆栈信息
先看下问题应用比较耗资源的线程
执行topHp12798
图片
找到前3个相对比较耗资源的线程jstack命令
jstack查看堆内存jstack12798grep12799的16进制31ff
图片
没看出什么问题,上下10行也看看,于是执行
图片
看到一些线程都是处于lock状态。但没有出现业务相关的代码,忽略了。这时候没有什么头绪。思考一番。决定放弃这次卡死状态的机器
为了保护事故现场先dump了问题进程所有堆内存,然后debug模式重启测试环境应用,打算问题再显时直接远程debug问题机器Tomcat远程debug
第二天问题再现,于是通知运维nginx转发拿掉这台问题应用,自己远程debugtomcat。
自己随意找了一个接口,断点在接口入口地方,悲剧开始,什么也没有发生!API等待服务响应,没进断点。这时候有点懵逼,冷静了一会,在入口之前的aop地方下了个断点,再debug一次,这次进了断点,f8N次后发现在执行redis命令的时候卡主了。继续跟,最后在到jedis的一个地方发现问题:ReturnsaJedisinstancetobeusedasaRedisconnection。Theinstancecanbenewlycreatedorretrievedfromapool。returnJedisinstancereadyforwrappingintoa{linkRedisConnection}。protectedJedisfetchJedisConnector(){try{if(usePoolpool!null){returnpool。getResource();}JedisjedisnewJedis(getShardInfo());forceinitialization(seeJedisissue82)jedis。connect();returnjedis;}catch(Exceptionex){thrownewRedisConnectionFailureException(CannotgetJedisconnection,ex);}}
上面pool。getResource()后线程开始waitpublicTgetResource(){try{returninternalPool。borrowObject();}catch(Exceptione){thrownewJedisConnectionException(Couldnotgetaresourcefromthepool,e);}}
returninternalPool。borrowObject();这个代码应该是一个租赁的代码,接着跟publicTborrowObject(longborrowMaxWaitMillis)throwsException{this。assertOpen();AbandonedConfigacthis。abandonedConfig;if(ac!nullac。getRemoveAbandonedOnBorrow()this。getNumIdle()2this。getNumActive()this。getMaxTotal()3){this。removeAbandoned(ac);}PooledObjectTpnull;booleanblockWhenExhaustedthis。getBlockWhenExhausted();longwaitTime0L;while(pnull){booleancreatefalse;if(blockWhenExhausted){p(PooledObject)this。idleObjects。pollFirst();if(pnull){createtrue;pthis。create();}if(pnull){if(borrowMaxWaitMillis0L){p(PooledObject)this。idleObjects。takeFirst();}else{waitTimeSystem。currentTimeMillis();p(PooledObject)this。idleObjects。pollFirst(borrowMaxWaitMillis,TimeUnit。MILLISECONDS);waitTimeSystem。currentTimeMillis()waitTime;}}if(pnull){thrownewNoSuchElementException(Timeoutwaitingforidleobject);}
其中有段代码if(pnull){if(borrowMaxWaitMillis0L){p(PooledObject)this。idleObjects。takeFirst();}else{waitTimeSystem。currentTimeMillis();p(PooledObject)this。idleObjects。pollFirst(borrowMaxWaitMillis,TimeUnit。MILLISECONDS);waitTimeSystem。currentTimeMillis()waitTime;}}
borrowMaxWaitMillis0会一直执行,然后一直循环了开始怀疑这个值没有配置
找到redispool配置,发现确实没有配置MaxWaitMillis,配置后else代码也是一个Exception并不能解决问题
继续F8publicEtakeFirst()throwsInterruptedException{this。lock。lock();Objectvar2;try{Objectx;while((xthis。unlinkFirst())null){this。notEmpty。await();}var2x;}finally{this。lock。unlock();}returnvar2;}
到这边发现lock字眼,开始怀疑所有请求api都被阻塞了
于是再次ssh服务器安装arthas,(Arthas是Alibaba开源的Java诊断工具)
执行thread命令
图片
发现大量httpnio的线程waiting状态,httpnio8083exec这个线程其实就是出来http请求的tomcat线程arthas
随意找一个线程查看堆内存
thread428
图片
这是能确认就是api一直转圈的问题,就是这个redis获取连接的代码导致的,
解读这段内存代码所有线程都在等53e5504e这个对象释放锁。于是jstack全局搜了一把53e5504e,没有找到这个对象所在线程。
自此。问题原因能确定是redis连接获取的问题。但是什么原因造成获取不到连接的还不能确定
再次执行arthas的threadb(threadb,找出当前阻塞其他线程的线程)
图片
没有结果。这边和想的不一样,应该是能找到一个阻塞线程的,于是看了下这个命令的文档,发现有下面的一句话
图片
好吧,我们刚好是后者减短连接超时时间
再次整理下思路。这次修改redispool配置,将获取连接超时时间设置为2s,然后等问题再次复现时观察应用最后正常时干过什么。
添加一下配置JedisConnectionFactoryjedisConnectionFactorynewJedisConnectionFactory();。。。。。。。JedisPoolConfigconfignewJedisPoolConfig();config。setMaxWaitMillis(2000);。。。。。。。jedisConnectionFactory。afterPropertiesSet();
重启服务,等待
又过一天,再次复现
ssh服务器,检查tomcataccesslog,发现大量api请求出现500,org。springframework。data。redis。RedisConnectionFailureException:CannotgetJedisconnection;nestedexceptionisredis。clients。jedis。exceptions。JedisConnectionException:Couldnotgetaresourcefromthepoolatorg。springframework。data。redis。connection。jedis。JedisConnectionFactory。fetchJedisConnector(JedisConnectionFactory。java:140)atorg。springframework。data。redis。connection。jedis。JedisConnectionFactory。getConnection(JedisConnectionFactory。java:229)atorg。springframework。data。redis。connection。jedis。JedisConnectionFactory。getConnection(JedisConnectionFactory。java:57)atorg。springframework。data。redis。core。RedisConnectionUtils。doGetConnection(RedisConnectionUtils。java:128)atorg。springframework。data。redis。core。RedisConnectionUtils。getConnection(RedisConnectionUtils。java:91)atorg。springframework。data。redis。core。RedisConnectionUtils。getConnection(RedisConnectionUtils。java:78)atorg。springframework。data。redis。core。RedisTemplate。execute(RedisTemplate。java:177)atorg。springframework。data。redis。core。RedisTemplate。execute(RedisTemplate。java:152)atorg。springframework。data。redis。core。AbstractOperations。execute(AbstractOperations。java:85)atorg。springframework。data。redis。core。DefaultHashOperations。get(DefaultHashOperations。java:48)
找到源头第一次出现500地方,
发现以下代码。。。。。。。CursorcstringRedisTemplate。getConnectionFactory()。getConnection()。scan(options);while(c。hasNext()){。。。。。,,}
分析这个代码,stringRedisTemplate。getConnectionFactory()。getConnection()获取pool中的redisConnection后,并没有后续操作,也就是说此时redis连接池中的链接被租赁后并没有释放或者退还到链接池中,虽然业务已处理完毕redisConnection已经空闲,但是pool中的redisConnection的状态还没有回到idle状态
图片
正常应为
图片
自此问题已经找到。小小总结
总结:springstringRedisTemplate对redis常规操作做了一些封装,但还不支持像ScanSetNx等命令,这时需要拿到jedisConnection进行一些特殊的Commands
使用stringRedisTemplate。getConnectionFactory()。getConnection()
是不被推荐的
我们可以使用stringRedisTemplate。execute(newRedisCallbackCursor(){OverridepublicCursordoInRedis(RedisConnectionconnection)throwsDataAccessException{returnconnection。scan(options);}});
来执行,
或者使用完connection后,用RedisConnectionUtils。releaseConnection(conn,factory);
来释放connection。
同时,redis中也不建议使用keys命令,redispool的配置应该合理配上,否则出现问题无错误日志,无报错,定位相当困难。
年节吃过头,专家推荐这道三红消食饮!三红消食饮不仅可以兑水当饮料,也可以当果酱直接吃。酸酸甜甜的口味,孩子们也都很喜欢。一些消化功能不良人群也可适当饮用!三红消食饮〔原料〕新鲜山楂1000克、新……
自主年审图像采集过不去?你需要AEyeNoPluginSet老自们,2023年网上签到(年度登记)开始一段时间了,我们这边儿的网签时间是2023年1月4日3月31日。我是昨天弄完的,上午找了个笔记本,按照自主通知群里的提示,捣鼓了……
中国科幻也受热捧,剧版三体海外受好评电视剧《三体》自开播以来,其豆瓣评分一路上涨,截至目前达到8。3分,被赞为中国科幻剧的新巅峰。红星新闻记者了解到,剧版《三体》也发行到了海外,在Youtube平台观看人数……
大美陕南,朱鹮之乡洋县位于陕西南部,处于我国南北气候的分界线上,冬无严寒,夏无酷暑,是陕西省唯一建有朱鹮和长青两个国家级自然保护区的地区,被誉为地球上同纬度生态最好的地区之一。洋县也是世界珍禽朱……
Shams雄鹿最近几天一直在追求范弗利特今天NBA交易截止日,雄鹿除通过三方交易引进心仪许久的贾伊克劳德外,没有其他动作。TheAthletic记者ShamsCharania在节目中透露,最近几天,他们一直在追求猛龙……
通信行程卡到底记录了什么信息?有专家建议,通信行程卡下线后,应删除其记录的信息。专家言之有理,确实应该删除。不但要删除,还要对运营商(指移动、联通、电信等)的利用其技术进行定位的行为进行约束。……
香港保险有什么吸引之处香港有很多独特金融优势,例如是全球数一数二的低税天堂,有稳健的货币制度,资金又自由流通,有成熟的股票市场等等,令香港成为国际上重要的金融中心,澳门一时三刻难以取代其地位。在大湾……
排坛老前辈不安分!86老者还在指点江山,对女排评头论足惹众怒中国女排在历史上多次获得世界冠军,我们成绩非常不错,中国女排由于成绩非常优异,所以吸引了很多球迷的关注,虽然在东京奥运会上,中国女排没有能够实现自己的冠军梦想,但是对于女排姑娘……
2岁女童舌头卷入电动指甲刀被卡,消防配合医护绣花式救援来源:荔枝新闻微博近日,江苏泰州消防接到求助,一不到2岁的女童舌头被指甲钳卡住无法拔出,急需救援。消防员发现,卡住孩子的是一款电动充电式指甲刀,刀片犹如一根根旋转的滚筒,……
Mitosis大火?AngularReactVueSvelt大家好,很高兴又见面了,我是前端进阶,由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!Mitosis:单组件编译成Vue、……
观观抽奖丨AI春联第二波开奖啦!快来围观视频加载中。。。四川观察原创话题活动,许愿春节,晒幸福春联今天第二波开奖啦,快来看看有没有你!获奖者为:2023年1月17日2023年1月28日14:00,在四川观……
有什么超有气质的句子推荐?1。hr阅己,越己,悦己。2。hr自行,自省,自醒。3。hr想开,看开,放开。4。hr无味,无谓,无畏。5。hr随心,随愿,随缘。6。hr知足,感……