简介:记一次TCP全队列溢出问题排查过程

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的请求

9: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队列溢出问题也随之解决。

作者:陈立华

原文链接

本文为阿里云原创内容,未经允许不得转载

多队列 部分队列没有包_记一次TCP全队列溢出问题排查过程相关推荐

  1. 多次执行sql 后卡住_解Bug之路记一次中间件导致的慢SQL排查过程

    解Bug之路-记一次中间件导致的慢SQL排查过程 前言 最近发现线上出现一个奇葩的问题,这问题让笔者定位了好长时间,期间排查问题的过程还是挺有意思的,就以此为素材写出了本篇文章. Bug现场 我们的分 ...

  2. sql 在某段时间_解Bug之路记一次中间件导致的慢SQL排查过程

    解Bug之路-记一次中间件导致的慢SQL排查过程 前言 最近发现线上出现一个奇葩的问题,这问题让笔者定位了好长时间,期间排查问题的过程还是挺有意思的,就以此为素材写出了本篇文章. Bug现场 我们的分 ...

  3. java内存溢出怎么排查_【转】Java学习---内存溢出的排查经历

    前言 OutOfMemoryError 问题相信很多朋友都遇到过,相对于常见的业务异常(数组越界.空指针等)来说这类问题是很难定位和解决的. 本文以最近碰到的一次线上内存溢出的定位.解决问题的方式展开 ...

  4. 如何获取握手包_白话详解TCP的三次握手到底做了些什么

    作 者:雨林 来 源:cnblogs.com/yuilin 广而告之:由于此订阅号换了个皮肤,系统自动取消了读者的公众号置顶.导致用户接受文章不及时.您可以打开订阅号,选择置顶(标星)公众号,重磅干货 ...

  5. python拦截数据包_使用Python进行TCP数据包注入(伪造)

    数据包注入是对已经建立的网络连接通过构建任意协议(TCP...UDP...)然后用原始套接字发送的方式进行妨碍的过程,这种方法被广泛使用在网络渗透测试中,比如DDOS,端口扫描等. 一个数据包由IP头 ...

  6. java获取达梦数据库_记一次对达梦数据库的优化过程

    某年某月某日的一个下午,接收到监控服务器的一条告警短信: 尊敬的运维工程师 XX,你好: "192.168.136.200"数据库服务器 CPU 异常,CPU 使用率 98.7%, ...

  7. python拦截tcp数据包_发送低级原始tcp数据包python

    我最近一直在做一个原始数据包的程序.我们最近有一个关于生包的讲座,所以我一直在努力学习和做我的教授告诉我的事情.我的程序有问题,它出现了一个错误,说目标地址是必需的,它是原始的,所以我不想做socke ...

  8. 手机联系人头像包_三星Galaxy M30(全网通)忘记了手机密码可怎么办?

    众所周知,手机已经成为了我们日常生活中的一部分.但是有些粗心大意的朋友,往往前一天晚上刚设置的密码,到第二天早上想要用手机的时候突然忘了手机密码,那么小编今天就教大家怎么解决这个问题.忘记了手机密码可 ...

  9. python会不会内存泄露_记一次python 内存泄漏问题及解决过程 python 嵌套读取文件 内存泄露...

    如何解决Python2的内存泄漏问题 python本身不会造成内存泄露,如果用的C/C++写的模块(包),如果处理不好,可能会造成内存泄露,但是也是C/C++的模块造成的 欢迎来到四十五资源网,pyt ...

最新文章

  1. tcp拥塞控制编程实验c语言代码,C语言 计算机网络TCP拥塞控制模拟程序
  2. 二叉树的深度_Golang面试题:二叉树的最大深度
  3. Docker file 搭建 Nginx镜像
  4. 怎么算matlab算法复杂度,算法复杂度分析
  5. 2017微服务 mysql集群_成功升P7多亏掌握了这几点:高并发+Nginx+微服务+Redis+MySQL...
  6. Android 系统(94)---android系统属性(ro.com.google.clientidbase.ms)随卡适配
  7. 街霸5 android,MD街头霸王5免安装版
  8. Hibernate实体对象的生命周期(三种状态详解)
  9. 字符串format函数使用
  10. Hibernate常用查询
  11. 基于PhalApi的Redis拓展
  12. Qt开源炫酷界面QCoolPage
  13. 7-77 打印学生选课清单 (25分)
  14. 【聚类算法】带你轻松搞懂K-means聚类(含代码以及详细解释)
  15. 使用SPACEDESK时iPad显示Connected-Display OFF解决方法
  16. 15.真实感图形——光照与明暗+光线跟踪+纹理映射+辐射度方法+阴影
  17. Ubuntu vi 方向键乱码 问题解决
  18. 智能卡 7816协议【转】
  19. 百度网盘不限速方法2020_亿寻——百度网盘不限速方法
  20. luogu p4995 跳跳

热门文章

  1. 我结婚了,我要用什么做个邀请函呢?【iVX无代码YYDS 06】
  2. java把对象转成json_java 把对象 对象转换成json字符串 | 学步园
  3. mysql的调试与分析_mysql日志管理分析调试实例_MySQL
  4. 又来了!深度学习PyTorch与TensorFlow到底哪家强?
  5. 来看一场 AI 重建的 3D 全息世界杯比赛!
  6. 数据分析师+做过名企项目+懂运营+985毕业=跳槽失败?
  7. ubuntu 新增mysql用户_Ubuntu中给mysql添加新用户并分配权限
  8. promise的状态以及api介绍_前端 api 请求缓存
  9. linux windows多任务,windows是多用户多任务系统吗?
  10. react dispatch_React测试的那些事(三) React Hook 测试实例