接口挂死,你咋办?

引子

周末本来是可贵惬意的时光,尤为是晚上,陪家人看看电视啥的再好不过了。不料,晚饭事后不久,手机就传来 ”叮“ 的一声,清脆悦耳,觉得有人发红包,定眼一看,不妙!—— 线上服务器告警。redis

连忙扒开电脑,准备 ”灭火“,根本没心思看电视。缓存

过程

登上服务器看日志,发现每隔 2 秒钟 tomcat 日志就会打印一次 ”Broken pipe“,就是这种:tomcat

似曾相识,以前有位同事作大量数据导出功能,就出现过,很显然是接口 response 耗时过长致使的。那接口是否是有问题了?服务器

用 postman 测试一下线上接口,点击 ”send“ 按钮后一直loading,等了老白天才有结果返回,耗时 1 min 左右。并发

打开手机端也没法获取数据了,一直在加载中。ide

嗯,事情不妙。post

深吸一口气,冷静 ~测试

告诫本身再看一遍告警短信:redis 链接池异常。难道 redis.pool 配置过小不够用?请教了一下相关同事,他说 redis 没问题。idea

我打开 idea , 从新检查了一遍 redis 相关的配置,定义,使用,该检查的都检查了。日志

心想:既然是接口耗时,那我就把代码执行过程当中,每个步骤的执行时长先打印出来看看嘛,到底是哪里”占坑不拉屎“?

先理一理代码逻辑:

  1. 从 redis 拿 IdList (大约2万)
  2. 遍历 IdList 从 DB 取数据 Entities,并组装为 EntityVos(组装信息较多,需从其余 rpc 接口获取)
  3. pageUtils 返回分页数据

我发现第 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 就会更顺利。 二、虽然改了一点逻辑,但同时顺便重构了很多代码,重构应该发生在每时每刻才对。

(完)
相关文章
相关标签/搜索