hi,all
 
导读:9月1号17:12左右,发现影子队列存在大量“unacked”(收到了消息,但是还没有手动确认消息)的消息,一段时间后“unacked”的数量没有减少,但是观察消费者端的日志,并没有新消息进来,
原因竟是。。。
 
关键词:rabbitmq,Tcp Window full
问题背景:
9月1号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://blog.csdn.net/james_searcher/article/details/70308565)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。
 
 
结束语:
rabbitmq的使用我们还处于初学者阶段,使用之前一定要多看rabbitmq的api,熟悉常用api的用法,在线下多做实验。

rabbitmq消费者“无故消失”的更多相关文章

  1. RabbitMQ~消费者实时与消息服务器保持通话

    这个文章主要介绍简单的消费者的实现,rabbitMQ实现的消费者可以对消息服务器进行实时监听,当有消息(生产者把消息推到服务器上之后),消费者可以自动去消费它,这通常是开启一个进程去维护这个对话,它与 ...

  2. RabbitMq消费者在初始配置之后进行数据消费

    RabbitMq消费者在初始配置之后进行数据消费 问题背景 在写一个消费rabbitmq消息的程序是,发现了一个问题,消费者的业务逻辑里面依赖这一些配置信息,但是当项目启动时,如果队列里面有积压数据的 ...

  3. supervisor安装以及监控管理rabbitmq消费者进程

    简介:Supervisor是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启. 1.安装 apt-get install ...

  4. RabbitMQ消费者消失与 java OOM

    原因: 下午先是收到钉钉告警有一个消费者系统任务积压, 当时以为就是有范围上量没有当回事,后来客服群开始反馈说有客户的数据没有生成.这个时候查看mq的后台,发现任务堆积数量还是很多. 这个时候登录一台 ...

  5. 2017年5月11日17:43:06 rabbitmq 消费者队列

    从昨天开始发现个问题,一个接口在本地调用时大部分正常,一旦在生成者打一个断点调试,并且在promotion也打断点的时候会出现没有返回channel的异常,然后消费者就再也消费不了了 16:57:45 ...

  6. RabbitMQ消费者消息权重,

    有的消费者处理速度快,有的消费者处理速度慢,我们想给处理快的多发消息,处理慢的少发消息, 怎么办呢?按照之前的轮询模式,肯定不行的,这里可以检测消息数量,如果消费者正在处理就不给他发 .... def ...

  7. RabbitMQ消费者抛异常日志持续打印的问题

    场景 消费者接受消息,进行一系列处理,但是由于某些原因处理过程中该消费者的抛出了异常,并且不捕获(直接 throws IOException 抛出去): 由于抛出了IOException,那么这条消息 ...

  8. RabbitMQ 消费者的消息确认机制

    消息确认的几点说明: 1. Basic.Ack 发回给 RabbitMQ 以告知,可以将相应 message 从 RabbitMQ 的消息缓存中移除.2. Basic.Ack 未被 consumer ...

  9. 十五、.net core(.NET 6)搭建RabbitMQ消息队列生产者和消费者的简单方法

    搭建RabbitMQ简单通用的直连方法 如果还没有MQ环境,可以参考上一篇的博客,在windows系统上的rabbitmq环境搭建.如果使用docker环境,可以直接百度一下,应该就一个语句就可以搞定 ...

随机推荐

  1. Spring Boot工程支持HTTP和HTTPS,HTTP重定向HTTPS

    本文试图以通俗易通的方式介绍Https的工作原理,不纠结具体的术语,不考证严格的流程.我相信弄懂了原理之后,到了具体操作和实现的时候,方向就不会错,然后条条大路通罗马.阅读文本需要提前大致了解对称加密 ...

  2. 问题(一) DebugAugmenter

    问题: DebugAugmenter的作用是什么?是任何一个自创建的变量都可以取代它还是它有特定含义? public class DebugAugmenter Test { @Test public ...

  3. AutoIT 测试GUI工具

    今天听到同事提到AutoIT,可以用来测试GUI窗口.了解一下这个工具. 以下内容引自: http://www.jb51.net/article/14870.htm (此url非原出处,该博主未注明原 ...

  4. 单机部署-consul

    在本机开发环境中,直接通过以下命令可以启动一个单机consul服务器: ./consul agent -server -data-dir=/tmp/consul -bootstrap -adverti ...

  5. BZOJ3629(JLOI2014)聪明的燕姿

    (⊙﹏⊙)我交了好久,有坑啊...(如果没有匹配的话,即输出0种情况要记得换行...) 就是搜索,加上一点数论,并不太难... #include<cstdio> #include<c ...

  6. ELK---日志分析系统

    ELK就是一套完整的日志分析系统 ELK=Logstash+Elasticsearch+Kibana 统一官网https://www.elastic.co/products ELK模块说明 Logst ...

  7. Django 基础二(View和urls)

    上一篇博文已经成功安装了python环境和Django,并且新建了一个空的项目.接下来就可以正式开始进行Django下 的Web开发了.首先进入项目的主目录: cd ./DjangoLearn/hol ...

  8. 浅谈URL跳转与Webview安全

    学习信息安全技术的过程中,用开阔的眼光看待安全问题会得到不同的结论. 在一次测试中我用Burpsuite搜索了关键词url找到了某处url,测试一下发现waf拦截了指向外域的请求,于是开始尝试绕过.第 ...

  9. java一个大接口拆用多线程方式拆分成多个小接口

    问题引入 目的:我们的接口A  分别调用了a1 a2 a3 三个接口,最终返回值是 a1的返回值+a2的返回值+a3的返回值 如果同步执行 a1 a2 a3 然后结果相加 很慢 . 如果异步执行 无法 ...

  10. WebView,我已经长大了,知道自己区分是否安全了!

    一.前言 如果你在用 Android 原生系统(Google Play 服务),在使用 WebView 加载某些网页时,一定遇到过以下的安全警告红屏. 这是 WebView 的安全浏览保护策略,在 A ...