周末本来是可贵惬意的时光,尤为是晚上,陪家人看看电视啥的再好不过了。不料,晚饭事后不久,手机就传来 ”叮“ 的一声,清脆悦耳,觉得有人发红包,定眼一看,不妙!—— 线上服务器告警。redis
连忙扒开电脑,准备 ”灭火“,根本没心思看电视。缓存
登上服务器看日志,发现每隔 2 秒钟 tomcat 日志就会打印一次 ”Broken pipe“,就是这种:tomcat
似曾相识,以前有位同事作大量数据导出功能,就出现过,很显然是接口 response 耗时过长致使的。那接口是否是有问题了?服务器
用 postman 测试一下线上接口,点击 ”send“ 按钮后一直loading,等了老白天才有结果返回,耗时 1 min 左右。并发
打开手机端也没法获取数据了,一直在加载中。ide
嗯,事情不妙。post
深吸一口气,冷静 ~测试
告诫本身再看一遍告警短信:redis 链接池异常。难道 redis.pool 配置过小不够用?请教了一下相关同事,他说 redis 没问题。idea
我打开 idea , 从新检查了一遍 redis 相关的配置,定义,使用,该检查的都检查了。日志
心想:既然是接口耗时,那我就把代码执行过程当中,每个步骤的执行时长先打印出来看看嘛,到底是哪里”占坑不拉屎“?
先理一理代码逻辑:
我发现第 2 步耗时特别猛,因而先把组装的某一项信息注释掉不去 rpc 拿了,这样作的依据就是:在我刚才部署打印日志以后不久,这个调用 rpc 的服务所有报 time-out。
立刻注释掉,上线。
嗯,看样子正常了,时间已经飙到10点多了,洗洗睡去。
晚上躺在床上在想,其实我能够对那个 rpc 的调用结果作一层 redis 缓存,减小请求频次应该会好不少。
次日,兴高采烈地来到办公室,同事直接问候早安——”好像我们的xx没数据啊,你看看?“
话音未落,个人手机”叮“地一声,清脆悦耳,这回我知道确定不是红包了,是”炸药“ ~
连忙扒开电脑,准备”灭火“,根本没心思跟别人说早安。
此次告警短信的内容不是 redis 相关的了,而是:JVMFullGC卡顿次数频繁。
我又重读了几遍接口代码,总以为哪里不对。哦~ 耗时的问题我是真的解决了吗?
因为打印日志并未去掉,我仔细分析了下:既然拿 2 万的实体类进行遍历组装,最后分页只返回 10 条数据,这。。。
假分页,害死人~
因而把分页逻辑放到组装信息以前来作,好比你最终是返回 10 条数据,那就是组装 10 条了,而不是像以前那样组装了 2 万条。
其实,缘由也显而易见了:并发高,组装 vo 时间过长,致使用到 redis 的资源也得不到释放,new出来的大对象也多,得不到回收,一个请求未响应下一个请求已来,越叠越多,最后挂死。产生的表象就是:接口耗时长,redis 链接池异常,JVMFullGC卡顿等。
晚上复盘,拿到两张 MinorGC 和 FullGC 总次数的监控图:
一、其实线上一产生问题,应该先怀疑内存或 CPU 是否占用过高,我是过小看此次问题了,否则第一时间拿到 dump 就会更顺利。 二、虽然改了一点逻辑,但同时顺便重构了很多代码,重构应该发生在每时每刻才对。