1. 前言
本文排查的问题是经典的TCP队列溢出问题,因TCP队列问题在操作系统层面没有显著的指标异样,容易被疏忽,故把排查过程分享给大家。
2. 问题形容
A服务调用B服务接口超时,B服务主机IOWAIT高,具体超时状况分为两种:
- A服务的申请在B服务日志中可查到,但B服务的响应工夫超过了A服务的期待超时工夫3S。
- A服务的申请在B服务日志中无奈查到。
3. 问题剖析
此种超时申请集中在很短的一段时间(通常在2分钟之内),过后便恢复正常,所以很难抓到问题现场剖析起因,只能搭建测试环境,A服务继续申请B服务,在B服务主机上通过DD命令写入大量数据造成主机IOWAIT高,同时通过TCPDUMP在两端抓包剖析。
局部服务超时日志:
- 服务A:Get http://xxx&id=593930: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
- 服务B: "GET xxx&id=593930 HTTP/1.1" 200 64 "-" "Go-http-client/1.1" "-" "-" 165000(单位微秒)
服务A发动申请3S后没有收到服务B响应,断开连接,服务B日志显示解决时长为0.165S,远低于3S,服务A侧看服务B的响应工夫为网络传输工夫、TCP队列排队工夫及服务B利用程序处理工夫之和,因为是内网测试,网络传输工夫能够疏忽,次要排查方向应为TCP队列排队工夫。
4. 抓包数据分析
情景1:服务A及服务B均有连贯日志打印。
服务A端数据包剖析:
09:51:43.966553000 服务A发动 GET申请的数据包如下:
图1:服务A发动GET申请
09:51:46.966653000 服务A发动 GET申请3s(即服务A设置的期待超时时长)后,因未收到服务B响应,服务A向服务B发动FIN被动断开连接。
图2:服务A期待超时被动断开连接
09:51:59.958195000 服务A发动http申请16s后收到服务B的http响应报文,因服务A已被动敞开该连贯,故间接回复RST。
图3: 服务B16s后响应
服务B端数据包剖析:
09:51:44.062095000 服务B收到服务A发送的http申请包。
图4:服务B收到服务A的申请
09:51:59.936169000 服务B响应服务A,服务B从接管到http申请报文至响应http申请总用时约为15s多,但服务B打印的日志响应时长约为0.165s。
图5:服务B15S后响应
图6:服务B日志显示响应工夫0.165s
情景2:服务A有连贯日志,服务B无连贯日志。
服务A端数据包剖析:
09:51:43.973791000 服务A向服务B发送一个http申请数据包,随后收到服务B重传的第二次握手的syn+ack包,超过3s未收到服务B的http响应后断开连接。
图7:服务B重传syn+ack
服务B端数据包剖析:
服务B重传了第二次握手的syn+ack包,收到服务A的http申请,服务B疏忽,未响应,服务A期待超时后断开了连贯。
图8: 服务B疏忽服务A申请
5. 根因剖析
TCP在三次握手过程中内核会保护两个队列:
- 半连贯队列,即SYN队列
- 全连贯队列,即ACCEPT队列
图9:TCP队列
TCP三次握手过程中,第一次握手server收到client的syn后,内核会把该连贯存储到半连贯队列中,同时回复syn+ack给client(第二次握手),第三次握手时server收到client的ack,如果此时全连贯队列未满,内核会把连贯从半连贯队列移除,并将其增加到 accept 队列,期待利用过程调用 accept 函数取出连贯,如果全连贯队列已满,内核的行为取决于内核参数tcp_abort_on_overflow:
- tcp_abort_on_overflow=0,server会抛弃client的ack。
- tcp_abort_on_overflow=1,server 会发送 reset 包给 client。
默认值是0。
情景1的抓包数据显示连贯曾经进入全连贯队列,然而服务B日志显示的连接时间晚了15S多,阐明连贯在队列里期待了15S后才被利用解决。
情景2的抓包数据显示全连贯队列已溢出,内核依据tcp_abort_on_overflow的值为0抛弃了服务A的ack,超过了服务A的超时等待时间。
论断:服务B主机在IO达到瓶颈的状况下,零碎CPU工夫次要耗费在期待IO响应及解决软中断上,服务B应用程序获取的CPU工夫无限,无奈及时调用 accept 函数把连贯取出并解决,导致TCP全队列溢出或队列等待时间过长,超过了服务A的超时工夫。
6. 如何察看和调整tcp全队列
图10: TCP全队列察看办法
当连贯处于listen状态时:
- Recv-Q:目前全连贯队列的大小
- Send-Q:目前全连贯最大队列长度
当Recv-Q > Send-Q时示意全队列溢出,可通过执行netstat -s | grep "overflowed"命令察看溢出状况,查看累计溢出次数,如果需察看一段时间内的全队列溢出状况,倡议应用监控零碎采集数据,比方prometheus。
图11: TCP队列溢出监控
TCP 全连贯队列最大值取决于min(somaxconn, backlog),其中:
- somaxconn可通过内核参数/proc/sys/net/core/somaxconn设置,默认值是128。
- backlog是 listen(int sockfd, int backlog) 函数中的 backlog 大小,Nginx 默认值是 511,能够通过批改配置文件设置其长度。
7. 结语
本次问题,因为服务对成功率要求很高,所以先通过调大服务B主机/proc/sys/net/core/somaxconn参数值及服务A的超时工夫来缓解超时问题,临时保障了接口成功率。但要从根本上解决问题,仍需解决诱因io瓶颈,因为服务B主机挂载的共享sas存储集群上有其余客户的主机偶然io很大,影响了整个集群的性能。为解决此问题,更换为独享的ssd盘,并通过blktrace+fio剖析,将io调度算法批改为noop,io性能显著晋升,TCP队列溢出问题也随之解决。
原文链接 本文为阿里云原创内容,未经容许不得转载。