基本介绍
-
RocketMQ(MetaQ)是一款分布式、队列模型的消息中间件。基于发布订阅模式,有Push和Pull两种消费方式,支持严格的消息顺序,亿级别的堆积能力,支持消息回溯和多个维度的消息查询
-
MetaQ是内部孵化的消息产品,RocketMQ是其抽出内核开源的产品,所以基本概念和用法上差别较小,内部使用推荐MetaQ的SDK
整体结构
nameserver负责注册发现。
术语解释
文档地址:aliyuque.antfin.com/mqdevops/me…
| 术语 | 解释 |
|---|---|
| Message Queue | 消息队列,消息是发送到多个broker上的多个队列上 |
| Message | 消息,消息队列中信息传递的载体。 |
| Message ID | 消息的全局唯一标识,由系统自动生成,唯一标识某条消息 |
| Message Key | 消息的业务标识,由消息生产者(Producer)设置,唯一标识某个业务逻辑。 |
| Topic | 消息主题,一级消息类型,通过 Topic 对消息进行分类。 |
| Tag | 消息标签,二级消息类型,用来进一步区分某个 Topic 下的消息分类 |
| Producer | 消息生产者,也称为消息发布者,负责生产并发送消息。 |
| Consumer | 消息消费者,也称为消息订阅者,负责接收并消费消息 |
| Consumer ID | 即消费组consumer group;一类 Consumer 的标识,这类 Consumer 通常接收并消费一类消息,且消费逻辑一致,同一组内的订阅关系必须要一致。 |
| 集群消费 | 一个 Consumer ID 所标识的所有 Consumer 平均分摊消费消息。 |
| 广播消费 | 一个 Consumer ID 所标识的所有 Consumer 都会各自消费某条消息一次。 |
| 延时消息 | Producer 将消息发送到 MQ 服务端,但并不期望这条消息立马投递,而是延迟一定时间后才投递到 Consumer 进行消费,该消息即延时消息。 |
消息幂等
消费幂等的必要性
在互联网应用中,尤其在网络不稳定的情况下,消息有可能会出现重复,可以概括为以下两种情况:
- 发送时消息重复【消息 Message ID 不同】:
消息已成功发送到服务端并完成持久化,此时网络闪断或者客户端宕机导致服务端应答给客户端失
败。如果此时 MQ Producer 意识到消息发送失败并尝试再次发送消息,MQ 消费者后续会收到两条
内容相同但是 Message ID 不同的消息。
- 消费时消息重复【消息 Message ID 相同】;
消息已投递到消费者并完成业务处理,当客户端给服务端反馈应答的时候网络闪断。为了保证消息
至少被消费一次,MQ 服务端将在网络恢复后再次尝试投递之前已被处理过的消息,MQ 消费者后
续会收到两条内容相同并且 Message ID 也相同的消息。
处理建议
基于上述第一种原因,内容相同的消息 Message ID 可能会不同,真正安全的幂等处理,不建议以
Message ID 作为处理依据。最好的方式是以业务唯一标识作为幂等处理的关键依据,而业务的唯一标识可以通过消息 Key 进行设置。
功能使用
消息发送demo
//创建生产者
MetaProducer producer = new MetaProducer("myProducer");
//Producer对象在使用之前必须要调用start初始化,初始化一次即可
producer.start();
//创建消息
Message msg = new Message("Jodie_topic_1023",// topic
"TagA",// tag
"OrderID001",// 消息的Key字段唯一标识消息的
("Hello MetaQ").getBytes());// body
//普通消息同步发送
SendResult send(Message msg)
//普通消息异步发送
void send(Message msg, SendCallback sendCallback)
//顺序消息同步发送
SendResult send(Message msg, MessageQueue mq)
//顺序消息异步发送
void send(Message msg, MessageQueue mq, SendCallback sendCallback)
消息消费demo
//创建push消费者
MetaPushConsumer consumer = new MetaPushConsumer("myConsumer");// 推模式consumer
//订阅指定topic下tags分别等于TagA或TagC或TagD
consumer.subscribe("TopicTest1", "TagA || TagC || TagD");
//普通消息消费
void registerMessageListener(final MessageListenerConcurrently messageListener)
//顺序消息消费
void registerMessageListener(final MessageListenerOrderly messageListener)
//广播消息订阅
consumer.setMessageModel(MessageModel.BROADCASTING);
void registerMessageListener(final MessageListenerConcurrently messageListener)
扩展
客户端:
aliyuque.antfin.com/nsq20p/nn5i…
监控规范;
aliyuque.antfin.com/nsq20p/nxl0…
快速上下线:
运维
接入申请
-
使用MetaQ无论发送还是订阅,请一定要在控制台走申请流程,不然影响消息收发的完整后果自负
-
写入量(普通消息)3000tps以上的业务接入前请先和消息团队联系,确认资源是否足够,否则会被直接禁读禁写
-
写入量(延迟level消息)1000tps以上的业务接入前请先和消息团队联系,确认资源是否足够,否则会被直接禁读禁写,尤其是饿了么配送相关业务;定时消息线上使用需要先邮件申请
-
广播消费请先发邮件申请,描述清楚业务场景、订阅的topic的写入峰值、消息体大小,以及广播的机器数
控制台
控制台地址:
日常
daily.ons.alibaba-inc.com/region/dail…
预发
pre-ons.alibaba-inc.com/region/pre/…
线上
q.alibaba-inc.com/region/sh/t…
创建topic
消息查询
发送trace日志
日志目录
/home/admin/logs/metaq/mqtrace
发送日志
pubTraceLog.log
格式
时间 traceid rpcid METAQ pubAfter logType group topic msgid originMsgId tags keys
buyerId trasnferFlag correctionFlag bodylength clientHost bornHost storeHost brokerName
queueid offset costtime isSuccess status resultCode errorMsg props regionId msgType
消费trace日志
消费日志
subTraceLog.log
格式
before
时间 traceid rpcid METAQ subBefore logType group topic msgid originMsgId
requestId tags keys buyerId trasnferFlag correctionFlag bodylength clientHost
bornHost storeHost storeTime brokerName queueid offset retryTime regionId
After
时间 traceid rpcid METAQ subAfter logType group topic msgid requestId clientHost
costTime isSuccess status resultCode errorMsg props regionId
消息存储
整体结构
高性能
- Commitlog顺序写
- MappedByteBuffer对文件进行读写操作。mmap将磁盘上的物理文件直接映射到用户态的内存地址
中(减少了传统IO将磁盘文件数据在操作系统内核地址空间的缓冲区和用户应用程序地址空间的
缓冲区之间来回进行拷贝的性能开销)正因为需要使用内存映射机制,故RocketMQ的文件存储都
使用定长结构来存储,方便一次将整个文件映射至内存
- OS使用PageCache机制对读写访问操作进行了性能优化
commitLog
- Commitlog文件存储 目录为/user.home/store/commitlog 目录
- 每一个文件默 认 lG,固定大小,方便内存映射
- 一个文件写满后再创建另外一个,以该文件中第一个偏移量为文件名,偏移量小于 20位用 0补齐
- 消息顺序写入日志文件
- 使用内存映射文件来提高 IO访问性能
- 采取定时删除存储文件的策略 ,默认保存72小时
消息格式
ConsumeQueue
- 消息消费队列,消息到达 CommitLog 文件后,将异步转发到消息消费队列,供消息消费者消费
- 存储路径/ user.home/store/ consumequeue/topic/queueId
- 单个 ConsumeQueue 文件中默认包含 30 万个条目,单个文件的长度为 30w × 20 字节
- 单个 ConsumeQueue文件可以看作是一个 ConsumeQueue条目的数组
- CommitLog的索引文件,支持按偏移量和时间查询
按时间查询逻辑:
1.根据时间戳定位到物理文件,从第一 个文件开始找到第一个文件更新时间大于该时间戳的文件
2.采用二分查找来加速检索
indexFile
其他文件
checkPoint文件
physicMsgTimestamp: commitlog文件刷盘时间点
logicsMsgTimestamp: 消息消费队列文件刷 盘时间 点
indexMsgTimestamp: 索引文件刷盘时间点
abort文件
启动时候创建,关闭时候销毁,判断broker是否正常退出
消息发送
发送方式
同步、异步、oneway、批量
发送流程
- 验证消息(topic,body等合法性、长度校验,body默认不超过4M)
- 查找主题路由信息
- 选择消息队列
sendWhichQueue 自增与路由表个数取模
1 ) sendLatencyFaultEnable=false,默认不启用 Broker故障延迟机制
2 ) sendLatencyFaultEnable=true,启用 Broker故障延迟机制 。
故障延迟会记录故障broker,按规避时间进行规避
- 消息发送
消息发送端采用重试机制 ,由 retryTimesWhenSendFailed 指定同步方式重试次数,异步重
试机制在收到消息发送结果后执行回调之前进行重试。 由retryTimesWhenSend-
AsyncFailed指定
默认重试两次一共3次
消息消费
消费概述
消息消费以组的模式开展,一个消费组内可以包含多个消费者,每一个消费组可订阅 多个主题,消费组之间有集群模式与广播模式两种消费模式 。
集群模式,主题下的同一条 消息只允许被其中一个消费者消费,一个消息 队列同一 时间只允许被一个消费者消费,一个消费者可以消费多个消息队列 。
广播模式,主题下的同一条消息将被集群内的所有消 费者消费一次。
消费模式
推模式(包装拉模式),拉模式
消息拉取流程
消息拉取客户端消息拉取请求封装,消息服务器查找并返回消息,消息拉取客户端处理返回的消息
流控
消费者本地缓存消息数超过pullThresholdForQueue时,默认1000。
消费者本地缓存消息大小超过pullThresholdSizeForQueue时,默认100MB。
消费者本地缓存消息跨度超过consumeConcurrentlyMaxSpan时,默认2000。
队列模型
消息模型
5.0以后实现的模型,比起上面能更好平衡。
队列负载与重新分布
集群模式队列负载
RebalanceService线程默认每隔 20s执行一次
(1) 从本地缓存变量中获取该Topic主题下的消息消费队列集合(mqSet);
(2) 根据topic和consumerGroup获取该消费组下消费者Id列表
(3) 对Topic下的消息消费队列、消费者Id排序,然后用消息队列分配策略算法计算出待拉取的消息队列。
分配算法
1 ) AllocateMessageQueueAveragely:平均分配
2 ) AllocateMessageQueueAveragelyByCircle:平均轮询分配
3 ) AllocateMessageQueueConsistentHash: 一致性 hash
4 ) AllocateMessageQueueByConfig:根据配置,为每一个消费者配置固定的消息队列
5 ) AllocateMessageQueueByMachineRoom:根据 Broker部署机房
- AllocateMachineRoomNearby:按靠近机器的机房的优先级
延迟消息
- 延迟级别默认值为“1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”,18个level。
- 定时消息会暂存在名为SCHEDULE_TOPIC_XXXX的topic中,并根据delayTimeLevel存入特定的queue,
queueId = delayTimeLevel – 1,即一个queue只存相同延迟的消息。
海量任意定时消息
如果是任意延迟时间,需要时间轮机制实现。
事务消息
主从同步-旧
主从切换-新
基于raft