TCP窗口被填满问题的排查实践

目录
  • 问题背景:
    • 应用程序(control)获取设备状态
    • 应用程序(control)下发设备指令
    • 临时解决方案:
    • 通过线下环境复现问题:
    • 现象复盘:
    • 终极解决方案:
  • 结束语:

问题背景:

某日17:12左右,收到实施人员投诉,有部分设备不能正常升级、收不到控制台下发的指令等问题,同事查看control工程(后面简称control)那边的日志,发现control没有收到设备上报的影子信息,所以没有下发指令。control工程直接对接设备,根据设备上报的信息对设备下发一些指令及配置信息,包括升级、上报日志等,IoT平台上线之前control依赖心跳上报来获取设备的当前信息,IoT平台上线之后依赖设备影子信息来获取设备的当前信息,control会订阅设备的影子信息,但影子信息是由影子服务(简称IoT)转发过去的,它不直接对接设备影子上报,具体流转细节,

请看这下面两个图:

应用程序(control)获取设备状态

应用程序(control)下发设备指令

得知control收不到影子消息以后,我立马去rabbitmq的控制台查看是否有消息,

确定两个事:

1.设备是否上报了消息

2.rabbitmq是否正常

下面图1、图2是当时截取的rabbitmq控制台的两个图,从图1可以很清楚的确定设备是有消息上报的,但是有很多消息是unacked(说明已经投递给了消费者,只是消费者没有ack而已,理论上等待一段时间就能正常)的,具体是哪个队列堆积unacked的消息请看图2,“spacebridgeiot-shadow”正是我们用来接收设备上报的影子信息的队列,消息都被堆积到队列了所以没有转发到control也是合理的,观察了一段时间发现unacked的数量变成了0,但是total的总数确没有太大变化,给人的感觉像是unacked的消息重新回到了消息队列里等待投递,果然过了几分钟以后又发现有大量unacked的消息,过了几分钟以后这部分unacked的消息重新回到队列里,control那边依然没有收到消息,这时查看IoT那边的日志发现竟然没有影子消息进来,在rabbitmq的控制台查看“spacebridgeiot-shadow”这个队列下居然没有消费者了,如图3所示。

这时查看rabbitmq的日志确实有错误信息,如图4所示,rabbitmq主动关闭了连接。

图1:rabbitmq概览图

图2:rabbitmq队列统计图

图3:spacebridgeiot-shadow 概览

图4:rabbitmq报错信息

临时解决方案:

由于当时已经有大量投诉过来了,所以采用了比较暴力的解决办法“将堆积的消息删除”,删除以后果然正常了(备注:线上问题必须尽快解决,没有时间允许我们去分析日志然后有条不紊的解决,必须快)。

通过线下环境复现问题:

  • 1.往10.200.41.166环境的rabbitmq的队列“mirrorTestQueue”堆积大量消息(起码万级)
  • 2.停掉mirrorTestQueue的消费者,待堆积完成以后重新启动
  • 3.堆积完成,重新启动消费者

和我们设想的一样,几秒内有几千条消息推给了消费者,持续几分钟以后rabbitmq主动关闭了和消费者之间的连接,这时从控制台看不到队列的消费者。

由于我们的消费者设置了自动恢复,所以过一阵又会自动连上,但很快又会被断连,和我们线上遇到的问题基本一样,究竟是什么导致了这个问题呢?说实话当时没有什么思路,网上找了一圈也没找到什么特别满意的答案(当时没有抓到问题的本质,搜的关键词太泛了),后来我们猜测可能是TCP层面出了什么问题,所以决定抓包试试能不能找到什么端倪。

果然,幸运的事情发生了,话不多说,直接上图。

13:06:25.643428之前rabbitmq还一直在给消费者推消息,直到13:06:25.643428这个时间点,开始出现消费者tcp窗口被打满的情况,大概持续了30秒左右,rabbitmq主动断开了连接(发了一个rst包),之后消费者重连,然后窗口又继续被打满,又持续30秒左右继续被断连。

感觉还挺有规律,每次持续30s,感觉是可配置的一个参数,大概总结一下就是“tcp full window导致了服务端主动rst连接,而且还有规律”

这次换了一下搜索的关键词找到了答案,rabbitmq有一个参数叫tcp_listen_options.send_timeout 是来控制写超时的一个参数,当写超时了以后就会触发tcp的RST(https://github.com/rabbitmq/rabbitmq-java-client/issues/341),修改一下试试效果如何:

1. 将写超时时间改成10s

tcp_listen_options.send_timeout = 10000

2.抓包看看是否起作用

可以看到从窗口满到关闭连接持续10s左右,说明这个参数是起作用的。

现象复盘:

由于rabbitmq的消费端没有设置prefetch所以rabbitmq一次性给消费端投递了过多的消息,从而导致消费端的 tcp 窗口被占满,进而触发了rabbitmq 的tcp_listen_options.send_timeout,这个写超时达到一个阈值后会触发rabbitmq断开消费者的tcp 连接。

终极解决方案:

之前删除消息只是迫不得已的方案,虽然解决了问题但太暴力,我们需要找到一个优雅的方案来应对,既然是推给消费者的消息太多造成了tcp窗口被打满,那我们就应该在接收速率上下点功夫,在连接rabbitmq的时候告诉它别给我发太多就行。

后面这段话摘自  https://www.jb51.net/article/236407.htm

rabbitmq有一个属性叫prefetch

prefetch是指单一消费者最多能消费的unacked messages数目。

如何理解呢?mq为每一个 consumer设置一个缓冲区,大小就是prefetch。每次收到一条消息,MQ会把消息推送到缓存区中,然后再推送给客户端。当收到一个ack消息时(consumer 发出baseack指令),mq会从缓冲区中空出一个位置,然后加入新的消息。但是这时候如果缓冲区是满的,MQ将进入堵塞状态。更具体点描述,假设prefetch值设为10,共有两个consumer。也就是说每个consumer每次会从queue中预抓取 10 条消息到本地缓存着等待消费。同时该channel的unacked数变为20。而Rabbit投递的顺序是,先为consumer1投递满10个message,再往consumer2投递10个message。如果这时有新message需要投递,先判断channel的unacked数是否等于20,如果是则不会将消息投递到consumer中,message继续呆在queue中。之后其中consumer对一条消息进行ack,unacked此时等于19,Rabbit就判断哪个consumer的unacked少于10,就投递到哪个consumer中。

具体到代码里就是

如何评估这个值呢,rabbitmq官方有个文章说的很好,就不细说了,我们的系统中目前设置的是20。

https://www.rabbitmq.com/blog/2012/05/11/some-queuing-theory-throughput-latency-and-bandwidth/

结束语:

对于关键组件的使用一定要熟悉其api,理解各个参数的含义和语法,当出现问题时不要局限于组件层面排查,必要的时候需要深入到底层,比如网络,操作系统等。

以上就是TCP窗口被填满问题的排查实践的详细内容,更多关于TCP窗口填满问题排查的资料请关注我们其它相关文章!

(0)

相关推荐

  • 使用Netty解决TCP粘包和拆包问题过程详解

    前言 上一篇我们介绍了如果使用Netty来开发一个简单的服务端和客户端,接下来我们来讨论如何使用解码器来解决TCP的粘包和拆包问题 TCP为什么会粘包/拆包 我们知道,TCP是以一种流的方式来进行网络转播的,当tcp三次握手简历通信后,客户端服务端之间就建立了一种通讯管道,我们可以想象成自来水管道,流出来的水是连城一片的,是没有分界线的. TCP底层并不了解上层的业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分. 所以对于我们应用层而言.我们直观是发送一个个连续完整TCP数据包的,

  • TCP关闭问题详细介绍

    摘要: 三次握手,四次挥手 意思是tcp建立连接时需要三次交互来完成,A发起连接 A --- SYN --> B A <-- SYN + ACK --- B (1) A --- ACK --> B 而关闭tcp连接需要四次交互,A发起关闭 A --- FIN --> B A <-- ACK --- B (1) A <-- FIN --- B A --- ACK --> B (2) 这里在(1)时B开始处于CLOSE_WAIT状态,一直到收到ACK后B才转为CLOS

  • 实例解说TCP连接建立及结束过程详解

    [简 介] TCP连接是面向可靠的连接,它通过建立可靠连接实现数据的可靠传输,在应用程序中被广泛使用.由于FTP命令采用的连接就是TCP连接,下面给大家介绍一下如何使用Sniffer工具捕获FTP命令数据包,分析TCP连接建立和结束的详细过程,使大家更好地理解和详细掌握TCP连接建立的三次握手过程和四次结束的过程.   一.FTP命令数据包的捕获 1.搭建网络环境.建立一台FTP服务器,设置IP地址为:76.88.16.16.建立一台FTP客户端,IP地址设为76.88.16.104,在其上安装

  • 详细讲解计算机网络——应用层

    目录 应用层协议 一.DNS 1.DNS 是什么 2.域名结构 3.域名服务器 4.DNS 解析流程 5.DNS 服务器查询方式 (1)迭代查询 (2)递归查询 6.DNS 缓存机制 7.DNS 使用 UDP 还是 TCP 区域传送(主.辅 DNS 服务器通信)时使用 TCP 域名解析时使用 UDP 二.万维网 1.万维网概述 (1)超媒体与超文本 (2)万维网的工作方式 (3)万维网必须解决的问题 2.超文本传送协议 HTTP (1)HTTP 的操作过程 (2)请求一个万维网文档所需的时间 3

  • C#中TCP粘包问题的解决方法

    一.TCP粘包产生的原理 1.TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾.出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成. 2.发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据.若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据.接收方引起的粘包是由于接收方用户进程不及时接收数据,从

  • TCP窗口被填满问题的排查实践

    目录 问题背景: 应用程序(control)获取设备状态 应用程序(control)下发设备指令 临时解决方案: 通过线下环境复现问题: 现象复盘: 终极解决方案: 结束语: 问题背景: 某日17:12左右,收到实施人员投诉,有部分设备不能正常升级.收不到控制台下发的指令等问题,同事查看control工程(后面简称control)那边的日志,发现control没有收到设备上报的影子信息,所以没有下发指令.control工程直接对接设备,根据设备上报的信息对设备下发一些指令及配置信息,包括升级.上

  • Android中ImageView.src设置图片拉伸、填满控件的方法

    问题 ImageView.src设置图片资源,图片不拉伸了,却有空隙部分: <LinearLayout android:id="@+id/linearLayout1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > <ImageView andro

  • 一次因Java应用造成CPU过高的排查实践过程

    前言 最近遇到一个java应用造成了服务器CPU使用率过高,最后查询,问题是因为在tomcat下重新部署应用的时候没有kill掉tomcat进程,造成应用中的数据库连接池进程中的锁不能被释放,死循环造成了cpu使用率过高的现象,详细原因就不做详细分析了,主要分享一下问题排查的过程. 使用top命令查询服务cpu使用情况 服务器资源使用率 可以看到31737这个进程的CPU使用率巨大 使用top -Hp 31737查询31737进程中各个线程的资源使用率 top -Hp 31737 使用top -

  • C#实现动态数字时钟和日历

    本文实例为大家分享了C#实现动态数字时钟和日历的具体代码,供大家参考,具体内容如下 实现如下图所示的简易时钟和日历,要求显示公历日期.时间.星期.农历日期. 首先新建一个ChineseCanlendar类用于实现和农历相关的操作: [ChineseCanlendar.cs] /*  * 作者:JeronZhou  * 时间:2021-11-01  * 功能:动态数字时钟和日历  */ using System; using System.Linq; using System.Globalizat

  • TCP三次握手及原理

    TCP/IP是很多的不同的协议组成,实际上是一个协议组,TCP用户数据报表协议(也称作TCP传输控制协议,Transport Control Protocol.可靠的主机到主机层协议.这里要先强调一下,传输控制协议是OSI网络的第四层的叫法,TCP传输控制协议是TCP/IP传输的6个基本协议的一种.两个TCP意思非相同. ).TCP是一种可靠的面向连接的传送服务.它在传送数据时是分段进行的,主机交换数据必须建立一个会话.它用比特流通信,即数据被作为无结构的字节流. 通过每个TCP传输的字段指定顺

  • Android编程处理窗口控件大小,形状,像素等UI元素工具类

    本文实例讲述了Android编程处理窗口控件大小,形状,像素等UI元素工具类.分享给大家供大家参考,具体如下: /** * 处理窗口控件大小,形状,像素等工具类 * * @author chen.lin * */ public class UITools { /** * 把像素转化为dp * * @param context * @param px * @return */ public static int px2dip(Context context, float px) { float d

  • 解决vue单页面多个组件嵌套监听浏览器窗口变化问题

    需求 最近公司有个大屏展示项目(如下图) 页面的元素需要做响应式监听,图表需要跟着窗口响应变化 问题 每一个图表都被我写成了一个组件,然后就在每一个组件里写了一串代码,监听浏览器变化 结果只有父组件的代码生效 mounted(){ window.onresize = () => { //当窗口发生改变时触发 // }; } 原因 经简单测试后发现,同一个路由页面只能注册一次浏览器窗口监听事件,第二次注册的会覆盖第一次注册 下边代码即可测试 mounted(){ window.onresize =

  • SQLServer数据库中开启CDC导致事务日志空间被占满的原因

    SQLServer中开启CDC之后,在某些情况下会导致事务日志空间被占满的现象为: 在执行增删改语句(产生事务日志)的过程中提示,The transaction log for database '***' is full due to 'REPLICATION'(数据库"***"的事务日志已满,原因为"REPLICATION"). CDC以及复制的基本原理粗略地讲,对于日志的使用步骤如下: 1,每当基础表(开启了CDC或者replication的表)产生事务性操作

  • 从Linux源码看Socket(TCP)Client端的Connect的示例详解

    前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的角度看下Client端的Socket在进行Connect的时候到底做了哪些事情.由于篇幅原因,关于Server端的Accept源码讲解留给下一篇博客. (基于Linux 3.10内核) 一个最简单的Connect例子 int clientSocket; if((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {

  • vue video和vue-video-player实现视频铺满教程

    网页放置视频播放器,我一般都是用video.js和它的插件vue-video-player 一:Video.js 需求:对于简单的视频播放需求来说,video.js足以胜任了. 它可是支持HTML5和Flash的视频播放器呦. 1:安装video.js npm install -s video.js 2:在main.js文件中引入相关文件 import Video from 'video.js' import 'video.js/dist/video-js.css' Vue.prototype.

随机推荐