Topic 是 RocketMQ 中消息传输和存储的顶层容器,用于表示同一类业务逻辑的消息。比如:我们将通知类消息和业务类消息进行隔离,就可以使用不同的主题。也可以通过主题的分隔来做身份识别和鉴权等。
RocketMQ 5.x 版本开始,不建议使用自动添加主题,主题作为 RocketMQ 的顶层容器,需要严格合理的创建与管理,而且主题的创建与管理会占用一定的系统资源,所以特别是在生产环境下增删改查都不要随意进行。
5.1.3 版本的 RocketMQ目前是开启字段添加主题的,可以配置 borker 参数 autoCreateTopicEnable 为 false 来禁用。关于如何配置,我们会在后续文章中说明。
RocketMQ 支持的主题消息类型如下:
消息是按到达服务端的先后顺序存储在指定主题的多个队列中,每条消息在队列中都有一个唯一的Long类型坐标,这个坐标被定义为消息位点。
队列中最早一条消息的位点为最小消息位点(MinOffset);最新一条消息的位点为最大消息位点(MaxOffset)。
通过主题、队列和位点就可以定位任意一条消息的位置。
虽然消息队列逻辑上是无限存储,但由于服务端物理节点的存储空间有限, RocketMQ 会滚动删除队列中存储最早的消息。因此,消息的最小消费位点和最大消费位点会一直递增变化。
RocketMQ 通过消费位点管理消息的消费进度。每条消息被某个消费者消费完成后不会立即在队列中删除,RocketMQ 会基于每个消费者分组维护一份消费记录,该记录指定消费者分组消费某一个队列时,消费过的最新一条消息的位点,即消费位点。
队列中消息位点MinOffset、MaxOffset和每个消费者分组的消费位点ConsumerOffset的关系如下:
- ConsumerOffset≤MaxOffset:
- 当消费速度和生产速度一致,且全部消息都处理完成时,最大消息位点和消费位点相同,即ConsumerOffset=MaxOffset。
- 当消费速度较慢小于生产速度时,队列中会有部分消息未消费,此时消费位点小于最大消息位点,即ConsumerOffset
- ConsumerOffset≥MinOffset:正常情况下有效的消费位点ConsumerOffset必然大于等于最小消息位点MinOffset。消费位点小于最小消息位点时是无效的,相当于消费者要消费的消息已经从队列中删除了,是无法消费到的,此时服务端会将消费位点强制纠正到合法的消息位点。
RocketMQ 定义消费位点的初始值为消费者首次获取消息时,该时刻队列中的最大消息位点。相当于消费者将从队列中最新的消息开始消费。消费点位保存在服务端,与消费者无关(这样可以支持跨消费者的消费恢复)
在实际应用中,我们可能重置消费点位,以跳过某些消息不消费,或者恢复已消费的消息,这个我们后面再讲。
mqadmin 命令工具可以管理主题、集群、borker、消息、NameServer 等
几乎所有的 mqadmin 命令都有 -n 和 -c 命令
$> ./mqadmin updateTopic -h
结果
usage: mqadmin updateTopic [-a <arg>] -b <arg> | -c <arg> [-h] [-n <arg>] [-o <arg>] [-p <arg>] [-r <arg>]
[-s <arg>] -t <arg> [-u <arg>] [-w <arg>]
-a,--attributes <arg> attribute(+a=b,+c=d,-e) # Topic 的属性,如 message.type 消息的类型
-b,--brokerAddr <arg> create topic to which broker # broker 地址
-c,--clusterName <arg> create topic to which cluster # 集群名称,默认集群名称为:DefaultCluster
-h,--help Print help # 帮助选项
-n,--namesrvAddr <arg> Name server address list, eg: '192.168.0.1:9876;192.168.0.2:9876' # nameServer 地址
-o,--order <arg> set topic's order(true|false) # 是否为顺序消息主题,此设置需要与 message.type 消息的类型对应
-p,--perm set topic' s permission(2|4|6), intro[2:W 4:R; 6:RW; 0:不可读写] # 指定新topic的读写权限( W=2|R=4|WR=6)
-r,--readQueueNums <arg> set read queue nums # 可读队列大小(默认为8),
-s,--hasUnitSub <arg> has unit sub (true|false)
-t,--topic <arg> topic name # 主题名称
-u,--unit <arg> is unit topic (true|false)
-w,--writeQueueNums <arg> set write queue nums # 可写队列大小(默认为8)
-r 和 -w 是读写队列的大小,队列数量的设置应遵循少用够用原则,避免随意增加队列数量。
-s 和 -u 官方文档没有说明,查看 tools 源码后,其对应的代码如下:
最后找到的解释为 sysFlag 是 Topic 的系统标志,
$> ./mqadmin updatetopic -n localhost:9876 -c DefaultCluster -t TestTopic -a +message.type=NORMAL
执行结果
TopicConfig [topicName=TestTopic, readQueueNums=8, writeQueueNums=8, perm=RW-, topicFilterType=SINGLE_TAG, topicSysFlag=0, order=false, attributes={+message.type=NORMAL}]
注:Topic命名应该尽量使用简短、常用的字符,避免使用特殊字符。特殊字符会导致系统解析出现异常,字符过长可能会导致消息收发被拒绝,最好对主题的命名制定相应的规则规范,避免滥用导致主题的管理上的混乱。
$> ./mqadmin topicList -n localhost:9876 -c DefaultCluster
$> ./mqadmin deleteTopic -h
usage: mqadmin deleteTopic -c <arg> [-h] [-n <arg>] -t <arg>
-c,--clusterName <arg> delete topic from which cluster
-h,--help Print help
-n,--namesrvAddr <arg> Name server address list, eg: '192.168.0.1:9876;192.168.0.2:9876'
-t,--topic <arg> topic name
$> ./mqadmin deleteTopic -n localhost:9876 -c DefaultCluster -t TestTopic
delete topic [TestTopic] from cluster [DefaultCluster] success.
delete topic [TestTopic] from NameServer success.
$> ./mqadmin topicStatus -h
usage: mqadmin topicStatus [-h] [-n <arg>] -t <arg>
-h,--help Print help
-n,--namesrvAddr <arg> Name server address list, eg: '192.168.0.1:9876;192.168.0.2:9876'
-t,--topic <arg> topic name
$> ./mqadmin topicStatus -n localhost:9876 -t TestTopic
#Broker Name #QID #Min Offset #Max Offset #Last Updated
broker-a 0 0 0
broker-a 1 0 0
broker-a 2 0 0
broker-a 3 0 0
broker-a 4 0 0
broker-a 5 0 0
broker-a 6 0 0
broker-a 7 0 0
- Broker Name:Broker 的名称
- QID:主题中的队列id(上面说过,主题中默认8个队列)
- Min offset:最小消息位点
- Max offset:最大消息位点,跟当队列收到的消息数量相关
- Last Updated:当前队列最后一次修改的时间
$> ./mqadmin topicRoute -h
usage: mqadmin topicRoute [-h] [-l] [-n <arg>] -t <arg>
-h,--help Print help
-l,--list Use list format to print data
-n,--namesrvAddr <arg> Name server address list, eg: '192.168.0.1:9876;192.168.0.2:9876'
-t,--topic <arg> topic name
$> ./mqadmin topicRoute -n localhost:9876 -t TestTopic
{
"brokerDatas":[
{
"brokerAddrs":{0:"192.168.1.1:10911"
},
"brokerName":"broker-a",
"cluster":"DefaultCluster",
"enableActingMaster":false
}
],
"filterServerTable":{},
"queueDatas":[
{
"brokerName":"broker-a",
"perm":6,
"readQueueNums":8,
"topicSysFlag":0,
"writeQueueNums":8
}
]
}
$> ./mqadmin topicRoute -n localhost:9876 -t TestTopic -l
#ClusterName #BrokerName #BrokerAddrs #ReadQueue #WriteQueue #Perm
DefaultCluster broker-a {0=192.168.1.1:10911} 8 8 6
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Total: 1 8 8
$> ./mqadmin topicClusterList -h
usage: mqadmin topicClusterList [-h] [-n <arg>] -t <arg>
-h,--help Print help
-n,--namesrvAddr <arg> Name server address list, eg: '192.168.0.1:9876;192.168.0.2:9876'
-t,--topic <arg> topic name
$> ./mqadmin topicClusterList -n localhost:9876 -t TestTopic
DefaultCluster
$> ./mqadmin updateTopicPerm -h
usage: mqadmin updateTopicPerm [-b <arg>] [-c <arg>] [-h] [-n <arg>] -p <arg> -t <arg>
-b,--brokerAddr <arg> create topic to which broker
-c,--clusterName <arg> create topic to which cluster
-h,--help Print help
-n,--namesrvAddr <arg> Name server address list, eg: '192.168.0.1:9876;192.168.0.2:9876'
-p,--perm <arg> set topic's permission(2|4|6), intro[2:W; 4:R; 6:RW]
-t,--topic <arg> topic name
$> ./mqadmin updateTopicPerm -n localhost:9876 -c DefaultCluster -t TestTopic -p 6
update topic perm from 6 to 6 in 192.168.1.1:10911 success.
注意:此处 -c 参数为必须(同 updateTopic 命令)
$> ./mqadmin allocateMQ -h
usage: mqadmin allocateMQ [-h] -i <arg> [-n <arg>] -t <arg>
-h,--help Print help
-i,--ipList <arg> ipList
-n,--namesrvAddr <arg> Name server address list, eg: '192.168.0.1:9876;192.168.0.2:9876'
-t,--topic <arg> topic name
$> ./mqadmin allocateMQ -n localhost:9876 -t TestTopic -i 192.168.1.1
{"result":{"192.168.1.1":[{"brokerName":"broker-a","queueId":2,"topic":"TestTopic"},{"brokerName":"broker-a","queueId":3,"topic":"TestTopic"},{"brokerName":"broker-a","queueId":4,"topic":"TestTopic"},{"brokerName":"broker-a","queueId":5,"topic":"TestTopic"},{"brokerName":"broker-a","queueId":6,"topic":"TestTopic"},{"brokerName":"broker-a","queueId":7,"topic":"TestTopic"},{"brokerName":"broker-a","queueId":0,"topic":"TestTopic"},{"brokerName":"broker-a","queueId":1,"topic":"TestTopic"}]}}
$> ./mqadmin statsAll -h
usage: mqadmin statsAll [-a] [-h] [-n <arg>] [-t <arg>]
-a,--activeTopic print active topic only
-h,--help Print help
-n,--namesrvAddr <arg> Name server address list, eg: '192.168.0.1:9876;192.168.0.2:9876'
-t,--topic <arg> print select topic only
$> ./mqadmin statsAll -n localhost:9876 -t TestTopic
#Topic #Consumer Group #Accumulation #InTPS #OutTPS #InMsg24Hour #OutMsg24Hour
TestTopic YourConsumerGroup 1 140.48 140.47 605272 605270
不使用 -t 参数的话,将会显示所有主题的情况
- Topic:主题名称
- Consumer Group:消费组
- Accumulation:当前的累积量(一般情况下值应该很小,如果值很大说明消费能力不足,需要查看消费者的情况)
- InTPS:写入的TPS
- OutTPS:消费的TPS
- InMsg24Hour :24小时内进入的消息量
- OutMsg24Hour:24小时内消费的消息量
将消费者停掉,用生产者发送几条消息,当前的积累量就会出现了。
生产者已经将消息发送到 RocketMQ 的服务端,但由于消费者的消费能力有限,未能在短时间内将所有消息正确消费掉,此时在服务端保存着未被消费的消息,该状态即消息堆积。