案情描述
现象:监控系统显示,官网的服务每个月总有那么几天会烧脑,单台服务器qps 20以内的前提下,从某一刻开始,node进程持续性消耗cpu100%,并且在剔除流量后,居高不下。
问题:cpu持续性被单个服务消耗,既影响服务本身功能和用户体验,也威胁到其他系统和进程的正常运行,更重要的是,存在随时发生生产故障的可能性
应急措施
之前出现问题时,在不能快速定位到具体原因的尴尬前提下,选择了快速执行B计划应急
具体措施:运维对官网服务扩容,通过增加集群数量分担部分压力(其实没什么流量压力,只是因为cpu100%的那几台服务,响应能力变弱了,搞得人和服务器都压力山大的样子),验证新集群可以正常提供服务后,接入流量,并摘除问题服务器的流量,以保证官网服务的正常
搜集证据
问题服务器摘除流量后,就可以开始尸检了,真正的生产debug就这样发生了,贼尴尬,运维开发协作,上演一场debug秀:
运维:
- 从监控入手,过滤出耗时请求,缩小定位范围
- 从问题服务器入手,观察cpu,网络等资源的消耗情况
- 追踪node进程的系统调用情况,企图了解真正消耗cpu底层调用
- 使用上古神器gdb debug进程
- 猜测
开发:
- 分析运维提供的耗时接口,查看是否存在耗时逻辑
- 使用更适配node应用的调试工具,企图生产debug(有兴趣的童鞋可以看下 node应用debug利器)
- 猜测
证据:所有相关的小伙伴都在尽可能的提取证据,以解开案情疑虑,以下列出证据
- pm2 monit,显示node进程cpu占比100%,但每个进程业务日志并不多(当时截图居然只截了左边一半),说明真正的业务压力并不大
- ss -ant | awk ‘{++s[$1]} END {for(k in s) print k,s[k]}’ ,问题服务器的网络连接状态统计如下
FIN_WAIT2 394
LISTEN 7
CLOSE_WAIT 5662
TIME_WAIT 768
ESTABLISHED 570
FIN_WAIT1 1
数据显示大部分网络连接处于close_wait状态,这个状态提供了什么信息呢,先看下这些网络状态的释意
LISTEN: 侦听来自远方的TCP端口的连接请求;
SYN-SENT: 在发送连接请求后等待匹配的连接请求;
SYN-RECEIVED: 在收到和发送一个连接请求后等待对方对连接请求的确认;
ESTABLISHED: 代表一个打开的连接;
FIN-WAIT-1: 等待远程TCP连接中断请求, 或先前的连接中断请求的确认;
FIN-WAIT-2: 从远程TCP等待连接中断请求;
CLOSE-WAIT: 等待从本地用户发来的连接中断请求;
CLOSING: 等待远程TCP对连接中断的确认;
LAST-ACK: 等待原来的发向远程TCP的连接中断请求的确认;
TIME-WAIT: 等待足够的时间以确保远程TCP接收到连接中断请求的确认;
CLOSE: 没有任何连接状态;
也就是说大部分连接在等待用户服务发送最后的连接中断的信号,也就是四次握手(挥手)的第三个阶段没有完成
- strace -ffp 11112 -o shial.log ,跟踪进程的系统调用和信号情况
主线程调用:
一个任务线程调用:
可以看到的是,主线程和任务线程都在频繁的进行futex调用,而且在反复处理两个内存地址,0x3f0da84,0x3f0da58,并且
FUTEX_WAIT_PRIVATE这个指令的响应一直是 EAGAIN (Resource temporarily unavailable)
11132线程
futex(0x3f0da84, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x3f0da80, {
FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
futex(0x3f0da58, FUTEX_WAKE_PRIVATE, 1) = 0
futex(0x3f0da84, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x3f0da80, {
FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
futex(0x3f0da58, FUTEX_WAKE_PRIVATE, 1) = 0
futex(0x3f0da84, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x3f0da80, {
FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
futex(0x3f0da58, FUTEX_WAKE_PRIVATE, 1) = 0
futex(0x3f0da84, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x3f0da80, {
FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
futex(0x3f0da58, FUTEX_WAKE_PRIVATE, 1) = 0
11135线程
futex(0x3f0da84, FUTEX_WAIT_PRIVATE, 17359164, NULL) = 0
futex(0x3f0da58, FUTEX_WAIT_PRIVATE, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable)
futex