写点什么

性能调优篇:困扰我半年之久的 RocketMQ timeout exception 终于破解了

  • 2022 年 4 月 21 日
  • 本文字数:1802 字

    阅读完需:约 6 分钟

[](()2.1 初步分析

上图中有两条非常关键日志:


  • invokeSync:wait response timeout exception


网络调用超时


  • recive response,but not matched any request


这条日志非常之关键,表示尽管客户端在获取服务端返回结果时超时了,但客户端最终还是能收到服务端的响应结果,只是此时客户端已经等待足够时间后放弃处理了。


关于第二条日志,我再详细阐述一下其运作机制,其实也是用一条链接发送多个请求的编程套路。一条长连接,向服务端先后发送 2 个请求,客户端在收到服务端响应结果时,怎么判断这个响应结果对应的是哪个请求呢?如下图所示:



客户端多个线程,通过一条连接发送了 req1,req2 两个请求,但在服务端通常都是多线程处理,返回结果时可能会先收到 req2 的响应,那客户端如何识别服务端返回的数据是对应哪个请求的呢?


解决办法是客户端在发送请求之前,会为该请求生成一个本机器唯一的请求 id(requestId),并且会采用 Future 模式,将 requestId 与 Future 对象放入一个 Map 中,然后将 reqestId 放入请求体中,服务端在返回响应结果时将请求 ID 原封不动的放入到响应结果中,当客户端收到响应时,先界面出 requestId,然后从缓存中找到对应的 Future 对象,唤醒业务线程,将返回结构通知给调用方,完成整个通信。


故从这里能看到,客户端在指定时间内没有收到服务端的请求,但最终还是能收到,矛头直接指向 Broker 端,是不是 Broker 有瓶颈,处理很慢导致的。

[](()2.2 Broker 端处理瓶颈分析

在我的“经验”中,RocketMQ 消息发送如果出现瓶颈,通常会返回各种各样的 Broker Busy,而且可以通过跟踪 Broker 端写入 PageCache 的数据指标来判断 Broker 是否遇到了瓶颈。


grep "PAGECACHERT" store.log


得到的结果类似如下截图:



温馨提示:上图是我本机中的截图,当时分析问题的时候,MQ 集群中各个 Broker 中这些数据,写入 PageCache 的时间没有超过 100ms 的。


正是由于良好的 pagecache 写入数据,根据如下粗糙的网络交互特性,我提出将矛盾点转移到网络方面:


![在这里插入图片描述](https://img-blog.csdnimg.cn/20210425231721525.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==,size_16,color_FF 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 FFFF,t_70#pic_center)


并且我还和业务方确定,虽然消息发送返回超时,但消息是被持久化到 MQ 中的,消费端也能正常消费,网络组同事虽然从理论上来说局域网不会有什么问题,但鉴于上述现象,网络 Java 开源项目【ali1024.coding.net/public/P7/Java/git】 组还是开启了网络方面的排查。


温馨提示:最后证明是被我带偏了。

[](()2.3 网络分析

通常网络分析有两种手段,netstat 与网络抓包。

[](()2.3.1 netstat 查看 Recv-Q 与 Send-Q

我们可以通过 netstat 重点观察两个指标 Recv-Q、Send-Q。



  • Recv-Q


tcp 通道的接受缓存区


  • Send-Q


tcp 通道的发送缓存区


在 TCP 中,Recv-Q 与 Send-Q 的作用如下图所示:



  • 客户端调用网络通道,例如 NIO 的 Channel 写入数据,数据首先是写入到 TCP 的发送缓存区,如果发送发送区已满,客户端无法继续向该通道发送请求,从 NIO 层面调用 Channel 底层的 write 方法,会返回 0,表示发送缓冲区已满,需要注册写事件,待发送缓存区有空闲时再通知上层应用程序可以发消息。

  • 数据进入到发送缓存区后,接下来数据会随网络到达目标端,首先进入的是目标端的接收缓存区,如果与 NIO 挂钩的化,通道的读事件会继续,应用从接收缓存区中成功读取到字节后,会发送 ACK 给发送方。

  • 发送方在收到 ACK 后,会删除发送缓冲区中的数据,如果接收方一直不读取数据,那发送方也无法发送数据。


网络同事分布在客户端、MQ 服务器上通过每 500ms 采集一次 netstat ,经过对采集结果进行汇总,出现如下图所示:



从客户端来看,客户端的 Recv-Q 中会出现大量积压,对应的是 MQ 的 Send-Q 中出现大量积压。


从上面的通讯模型来看,再次推断是否是因为客户端从网络中读取字节太慢导致的,因为客户端为虚拟机,从 netstat 结果来看,疑似是客户端的问题(备注,其实最后并不是客户端的问题,请别走神)

[](()2.3.2 网络转包

总结

面试建议是,一定要自信,敢于表达,面试的时候我们对知识的掌握有时候很难面面俱到,把自己的思路说出来,而不是直接告诉面试官自己不懂,这也是可以加分的。


以上就是蚂蚁技术四面和 HR 面试题目,以下最新总结的最全,范围包含最全 MySQL、Spring、Redis、JVM 等最全面试题和答案,仅用于参考



用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
性能调优篇:困扰我半年之久的RocketMQ timeout exception 终于破解了_Java_爱好编程进阶_InfoQ写作社区