Topic太多,RocketMQ炸了!
网上博客常说,kafka的topic数量过多会影响kafka,而RocketMQ不会受到topic数量影响。
但是,果真如此吗?
最近排查一个问题,发现RocketMQ稳定性同样受到topic数量影响!!
好了,一起来回顾下这次问题排查吧,最佳实践和引申思考放在最后,千万不要错过。
1、问题描述
我们的RocketMQ集群为4.6.0版本,按照3个nameserver,2个broker,每个broker为主从双节点部署。
部署架构
某天收到警报,broker-b突然从nameserver掉线,且主从双节点都无法重新注册。
2、初步排查
2.1 检查进程存活&网络
因为控制台上显示broker-a正常,因此可以认为 nameserver、broker-a都是正常的,问题出在broker-b上。
当时第一反应是broker-b进程挂了,或者网络不通了。
登陆broker节点,看到进程依然存活。
然后通过telnet检查和nameserver的联通性,显示正常,网络没有问题。
2.2 检查日志
检查broker日志,马上发现了异常。
2023-01-09 14:07:37 WARN brokerOutApi_thread_3 - registerBroker Exception, mqnameserver3:xxxx
org.apache.rocketmq.remoting.exception.RemotingSendRequestException: send request to <qnameserver3/xx.xx.xx.xxx:xxxx> failed
at org.apache.rocketmq.remoting.netty.NettyRemotingAbstract.invokeSyncImpl(NettyRemotingAbstract.java:429) ~[rocketmq-remoting-4.6.0.jar:4.6.0]
at org.apache.rocketmq.remoting.netty.NettyRemotingClient.invokeSync(NettyRemotingClient.java:373)
......
异常比较明确,broker请求nameserver失败,所以导致无法注册到集群中。
那为什么会注册失败呢?没有非常明确的提示,因此去看下nameserver上的日志信息。
2023-01-09 14:09:26 ERROR NettyServerCodecThread_1 - decode exception, xx.xxx.xx.xxx:40093
io.netty.handler.codec.TooLongFrameException: Adjusted frame length exceeds 16777216: 16777295 - discarded
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.fail(LengthFieldBasedFrameDecoder.java:499) [netty-all-4.0.42.Final.jar:4.0.42.Final]
......
这个异常看起来是nameserver上的netty抛出的,请求过大抛出了异常。
根据日志关键字,直接定位到了源码,确实有默认的大小限制,并且可以通过
com.rocketmq.remoting.frameMaxLength进行控制。
2.3 源码分析
虽然找到了异常的直接原因,但是为什么broker突然会有这么大的请求?是什么带来的?
从broker的warning日志中,并没有办法看到更多有效信息。
因此,还是得深入分析下broker上的源码。根据日志关键字,很快找到broker中的异常位置
broker异常位置
注意!这里通过遍历nameserverlist,在线程池中异步注册,跟后面的一个小知识点有关。
从源码中可以分析出,如果有过大的请求的话,应该就是这个requestBody引起,它携带了大量topic信息topicConfigWrapper。
但是我们在控制台上看到当前集群中,只有300+topic(这里其实是一个误区,最后会解释),理论上来说是非常小的,为什么会超出容量限制呢?
看了下源码上下文,并没有对reqeustBody或者topicConfigWrapper有相关日志的记录,因此,还是需要arthas来看看了。
2.4 arthas定位
直接通过arthas定位实际内存值
watch org.apache.rocketmq.broker.out.BrokerOuterAPI registerBrokerAll {params,returnObj} -x 3
查看结果
内存中实际topic数量
啥玩意?!
topicConfigTable的map大小为size=71111?!!
进一步看看这些topic里面都是些啥?我们调整下arthas的参数-x为4,改变watch变量的深度。
发现问题了!
我们看到了大量%RETRY%开头的topic。
3、根本原因
至此,根本原因就能明确了。
RETRY topic过多,导致 broker 向 nameserver 发送心跳(定时发送注册请求)时,心跳请求中携带的 body 上的 topic 信息过大,超过了 nameserver 上使用的 NettyDecoder.java 限制的 16M (默认值),心跳请求失败,所以broker掉线。
4、恢复
既然问题基本确定了,那么先尝试恢复吧。
前面已经看到了对最大请求体的配置,因此,我们在bin/runserver.sh中添加一个JAVA_OPTION对
com.rocketmq.remoting.frameMaxLength进行配置。然后重启nameserver。
重新观察broker,果然重启成功了。
2023-01-09 16:03:55 INFO brokerOutApi_thread_3 - register broker[0]to name server mqnameserver4:9876 OK
2023-01-09 16:03:55 INFO brokerOutApi_thread_4 - register broker[0]to name server mqnameserver2:9876 OK
当然,这只是临时恢复措施,后面重点要思考以下问题并进行优化:
- RETRY topic数量这么多是否正常?是否可以清理无效topic?
- 如何做好后续的topic数量监控告警?
5、最佳实践
5.1 定时删除无效RETRY topic
考虑使用定时任务扫描所有业务topic下的消费组,再根据消费组状态(状态为not_online的消费组),拼出对应RETRY topic进行删除。以上步骤均有开源MQ sdk 的 api 可以调用。
即使后续消费组重新使用,RETRY topic 也会重新创建,不影响消费。
5.2 topic总数监控
前面说到在控制台上看到当前集群中只有300+topic,这里其实是一个误区,只勾选了NORMAL类型的topic,并没有注意RETRY、DLQ、SYSTEM类型的topic。
控制台误区
而这次几万个topic基本都是RETRY类型的。
后续需要添加topic数量监控(包括RETRY类型),防止由于topic数量过多,导致broker注册失败。
6、引申思考
6.1 RETRY topic是什么?为什么有这么多?
这需要从RocketMQ的重试机制与死信机制说起。
RocketMQ 提供了自带的重试机制,消息消费失败或超时,会被投递到 RETRY topic。RETRY topic 里的消息会按照延时队列的延时时间进行消费,这样也避免了有问题的消息阻塞正常消费。
RETRY topic 里保存的是消费状态为 consumer_later 的消息,在重试达到 16 次(默认值)以后,消息会进入死信队列(本质上也是一个新的topic类型,DLS topic)。
DLQ topic在使用时才会创建,因此不会像RETRY topic 这样大量膨胀。
但是,RETRY topic不一样。它是由RocketMQ服务端自动创建,创建的时机有两个:
- 消费失败的时候,将消息发送回 broker,这时候会在服务端创建RETRY topic
消费失败创建RETRY topic
- consumer client 和服务端保持心跳时创建RETRY topic
心跳时创建 retry topic
线下环境的消费组存在大量的临时测试group,而 RocketMQ会给每个实际存在的消费组创建RETRY topic,导致 RETRY topic 大量膨胀。
6.2 如果所有消息自动重试,顺序消息会乱序吗?
我们知道,RocketMQ中包含三种消息类型:普通消息、普通有序消息、严格有序消息。
三种消息的类型介绍如下:
- 普通消息:消息是无序的,任意发送发送哪一个队列都可以。
- 普通有序消息:同一类消息(例如某个用户的消息)总是发送到同一个队列,在异常情况下,也可以发送到其他队列。
- 严格有序消息:消息必须被发送到同一个队列,即使在异常情况下,也不允许发送到其他队列。
对于这三种类型的消息,RocketMQ对应的提供了对应的方法来分别消息:
//发送普通消息,异常时默认重试
public SendResult send(Message msg)
//发送普通有序消息,通过selector动态决定发送哪个队列,异常默认不重试,可以用户自己重试,并发送到其他队列
public SendResult send(Message msg, MessageQueueSelector selector, Object arg)
//发送严格有序消息,通过指定队列,保证严格有序,异常默认不重试
public SendResult send(Message msg, MessageQueue mq)
所以RocketMQ客户端的生产者默认重试机制,只会普通消息有作用。对于普通有序消息、严格有序消息是没有作用。
6.3 nameserver数据一致性问题
在通过修改启动参数
com.rocketmq.remoting.frameMaxLength进行临时恢复的时候,发现一个问题:日志恢复了,但是控制台上却仍然没有显示broker-b。
排查了下发现,由于nameserver有4台,只重启了一台,而控制台连接访问的nameserver是另一台,所以显示不正确。
通过切换控制台nameserver地址,就能看到broker-b了。
为什么不同nameserver允许数据不一致呢?
前面在排查的过程中也发现了,broker源码中通过遍历nameserverlist,在线程池中异步注册topic信息到nameserver。
注册逻辑
而这也体现了RocketMQ中对nameserver的设计思想。
nameserver是一个AP组件,而不是CP组件!
在 RocketMQ 中 Nameserver 集群中的节点相互之间不通信,各节点相互独立,实现非常简单。但同样会带来一个问题:
Topic 的路由信息在各个节点上会出现不一致。
那 Nameserver 如何解决这个问题呢?RocketMQ 的设计者采取的方案是不解决,即为了保证 Nameserver 的高性能,允许存在这些缺陷。
NameServer之间不通信,消息发送端通过PULL方式更新topic信息,无法及时感知路由信息的变化,因此引入了消息发送重试(只针对普通消息)与故障规避机制来保证消息的发送高可用。
事实上,在RocketMQ的早期版本,即MetaQ 1.x和MetaQ 2.x阶段,也是依赖Zookeeper的(CP型组件)。但MetaQ 3.x(即RocketMQ)却去掉了ZooKeeper依赖,转而采用自己的NameServer。
NameServer数据不一致,比较大的影响就是topic的队列会存在负载不均衡的问题,以及消费端的重复消费问题,这些问题对消息队列来说都是可以忍受的,只要最终能保持一致,恢复平衡即可。
都看到最后了,原创不易,点个关注,点个赞吧~
文章持续更新,可以微信搜索「阿丸笔记 」第一时间阅读,回复【笔记】获取Canal、MySQL、HBase、JAVA实战笔记,回复【资料】获取一线大厂面试资料。
知识碎片重新梳理,构建Java知识图谱:github.com/saigu/JavaK…(历史文章查阅非常方便)
Topic太多,RocketMQ炸了!的更多相关文章
- RocketMQ 创建和删除 topic,以及 broker 和 nameserver 之间的心跳
命令行主类:org.apache.rocketmq.tools.command.MQAdminStartup 客户端创建 topic 程序参数:updateTopic -n localhost:987 ...
- rocketmq 学习记录-2
产品选型 我们在进行中间件选型时,一般都是通过下面几点来进行产品选型的: 1.性能 2.功能支持程度 3.开发语言(团队中是否有成员熟悉此中间件的开发语言,市场上此种语言的开发人员是否好招) 4.有多 ...
- 「查缺补漏」巩固你的RocketMQ知识体系
Windows安装部署 下载 地址:[https://www.apache.org/dyn/closer.cgi?path=rocketmq/4.5.2/rocketmq-all-4.5.2-bin- ...
- rocketmq详解-[个人版]-第一章
一.消息队列概述 1.1.消息队列由来 在运维场景中,我们经常会存在如下场景:一旦出现S1异常,C1将因为S1的影响而异常(C为客户端,s为服务端) 当然可以通过添加多个S的方式,实现高可用.但这样会 ...
- RocketMQ学习笔记
RocketMQ学习笔记 RocketMQ学习参考官网文档:https://github.com/apache/rocketmq/tree/master/docs/cn . 由于RocketMQ是有国 ...
- 分布式开放消息系统(RocketMQ)的原理与实践
分布式消息系统作为实现分布式系统可扩展.可伸缩性的关键组件,需要具有高吞吐量.高可用等特点.而谈到消息系统的设计,就回避不了两个问题: 消息的顺序问题 消息的重复问题 RocketMQ作为阿里开源的一 ...
- RocketMQ最佳实践
1.RocketMQ中的专业术语 Topic topic表示消息的第一级类型,比如一个电商系统的消息可以分为:交易消息.物流消息...... 一条消息必须有一个Topic. Tag Tag表示消息的第 ...
- kafka删除topic的方法及我在kafka上边的一些经验
我在本地做kafka的producer调试,每隔一段时间后,所使用的topic管道就会堆积数据,而且我这边使用的是 kafka bin 下的consumer命令单独消费的,每次都是 --fro ...
- 分布式开放消息系统(RocketMQ)的原理与实践(转)
转自:http://www.jianshu.com/p/453c6e7ff81c 分布式消息系统作为实现分布式系统可扩展.可伸缩性的关键组件,需要具有高吞吐量.高可用等特点.而谈到消息系统的设计,就回 ...
- rocketMq概念介绍
rocketMq官网 http://rocketmq.apache.org/ rocketMq逻辑概念介绍 rocketMq逻辑图 备注: 改图片分享自李占卫的网上家园 说明: 在rocketM ...
随机推荐
- Solon 框架,单月下载量超100万了!!!
Solon 框架,于2023年的四月份突冲100万下载量了.感谢开源的力量,我们同喜同荣!!!Solon 目前,是"可信开源共同体"的新成员,积极参与中科院的"开源之夏& ...
- vue移动端适配方案
一.安装postcss-px-to-viewport插件 1.使用npm安装 $ npm install postcss-px-to-viewport --save-dev 2.或者使用yarn安装 ...
- 2021-05-25:给定一个矩阵matrix,值有正、负、
2021-05-25:给定一个矩阵matrix,值有正.负.0,蛇可以空降到最左列的任何一个位置,初始增长值是0,蛇每一步可以选择右上.右.右下三个方向的任何一个前进,沿途的数字累加起来,作为增长值: ...
- 2021-09-25:给定一个字符串数组,将字母异位词组合在一起。可以按任意顺序返回结果列表。字母异位词指字母相同,但排列不同的字符串。示例 1:输入: strs = [“eat“, “tea“, “
2021-09-25:给定一个字符串数组,将字母异位词组合在一起.可以按任意顺序返回结果列表.字母异位词指字母相同,但排列不同的字符串.示例 1:输入: strs = ["eat" ...
- linux 引导过程和服务控制
目录 一.引导分区 二.服务控制 三.运行级别 四.systemd初始化 五.模拟错误 一.引导分区 原理:引导分区是指在开机启动到进入系统这之间的过程 引导分区的过程:1.开机自检 自检顺序:BIO ...
- Odoo 13之十三 :开发之创建网站前端功能
Odoo 13开发之创建网站前端功能 Odoo 起初是一个后台系统,但很快就有了前端界面的需求.早期基于后台界面的门户界面不够灵活并且对移动端不友好.为解决这一问题,Odoo 引入了新的网站功能,为系 ...
- Linux常用磁盘管理命令详解
du du命令用于查看文件和目录磁盘的使用空间. 命令语法:du [参数] [文件或目录名称] 参数说明: 参数 说明 -a 列出所有的文件与目录容量. -h 以G.M.K为单位,返回容量. -s 列 ...
- RStuido Server 选择不同的 R 版本(conda 中的不同 R 版本)
自从上一次服务器重装系统之后,总感觉缺少了一些东西,安装R包很多依赖库报错,也可以解决,但总是存在,烦. 一天,一个同事问我说ggpubr包安装不成功,我就自己试了一下,真的是--安装不成功. 当你到 ...
- Linux 中 3 个文件打包上传和下载相关命令详解
tar 命令 通过 SSH 访问服务器,难免会要用到压缩,解压缩,打包,解包等,这时候tar 命令就是必不可少的一个功能强大的工具.Linux 中最流行的tar是麻雀虽小,五脏俱全,功能强大. 使用t ...
- .Net全网最简RabbitMQ操作【强烈推荐】
[前言] 本文自1年前的1.0版本推出以来,已被业界大量科技公司采用.同时也得到了.Net圈内多位大佬的关注+推荐,文章也被多家顶级.Net/C#公众号转载. 现在更新到了7.0版本,更好的服务各位. ...