上一篇认识了一下RocketMQ,本文讲讲讲RocketMQ生产端的那些事儿,消息的发送相关的原理。
消息发送的流程
RocketMQ 客户端的消息发送可以分为以下三层:
业务层:直接调用 MQ Client 发送 API 的业务代码;
消息处理层:RocketMQ Client 获取业务发送的消息对象后,一系列的参数检查、消息发送准备、参数包装等操作;
通信层:RocketMQ 基于 Netty 封装的一个 RPC 通信服务,RocketMQ 的各个组件之间的通信全部使用这个模块;
大概的流程:
- Broker启动时,向NameServer注册信息
- 客户端调用producer发送消息时,会先从NameServer获取该topic的路由信息。消息头code为GET_ROUTEINFO_BY_TOPIC
- 从NameServer返回的路由信息,包括topic包含的队列列表和broker列表
- Producer端根据查询策略,选出其中一个队列,用于后续存储消息
- 每条消息会生成一个唯一id,添加到消息的属性中。属性的key为UNIQ_KEY
- 对消息做一些特殊处理,比如:超过4M会对消息进行压缩
- producer向Broker发送rpc请求,将消息保存到broker端。消息头的code为SEND_MESSAGE或SEND_MESSAGE_V2(配置文件设置了特殊标志)
消息的数据结构
消息(Message)
消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于一个主题。RocketMQ 中每个消息拥有唯一的 Message ID,且可以携带具有业务标识的 Key。系统提供了通过 Message ID 和 Key 查询消息的功能。
核心字段配置
其他Message配置
Producer配置
消息发送方式
Rocketmq提供三种方式可以发送普通消息:同步、异步、和单向发送。
- 同步:发送方发送消息后,收到服务端响应后才发送下一条消息
- 异步:发送一条消息后,不等服务端返回就可以继续发送消息或者后续任务处理。发送方通过回调接口接收服务端响应,并处理响应结果。
- OneWay:发送方发送消息,不等待服务端返回响应且没有回调函数触发,即只发送请求不需要应答。
发送方式对比:发送吞吐量,单向>异步>同步。但单向发送可靠性差存在丢失消息可能,选型根据实际需求确定。
2、消息类型
消息客户端提供多种SDK:普通、顺序、事务、延时消息
Producer负载均衡
producer在发送消息时,默认轮询所有queue,消息就会被发送到不同的queue上。而queue可以分布在不同的broker上。
生产者高可用
【应用场景】
假如现在有个由三个 broker 节点组成的集群,有 topic1,默认在每个 broker 上创建 4 个队列,分别是:master-a(q0,q1,q2,q3)、master-b(q0,q1,q2,q3)、master-c(q0,q1,q2,q3),上一次发送消息到 master-a 的 q0 队列,此时 master-a 宕机了,如果继续发送 topic1 消息,如果避免再次发送到 master-a?
rocketmq 的解决方案:
发送失败重试和 Broker 故障延迟规避机制。通过配置项 retryTimesWhenSendFailed 来表示同步重试次数,默认为 2 次,加上正常发送 1 次,总共三次机会;选择队列的方式通过 sendLatencyFaultEnable 的值来控制,默认值为 false,不启动 broker 故障延迟机制,值为 true 时启用 broker 故障延迟机制。
(1)发送失败重试
RocketMQ 支持同步、异步发送,无论哪种方法都可以在失败后重试,如果单个 Broker 发生故障,重试会选择其他 Broker 保证消息的正常发送。
失败重试的逻辑:
(2)Broker 规避机制
RocketMQ Client 会维护一个“Broker-发送延迟”关系,根据这个关系选择一个发送延迟级别较低的 Broker,这样能最大限度地利用 Broker 的能力,剔除已经宕机、不可用或发送延迟级别较高的 Broker,尽可能保证消息正常发送。
NameServer挂了怎么办?
如果Namesrv挂了,当新加入的生产消费则获取不到topic路由信息会报MQExecption;如果生产消费缓存了生产者有缓存 Topic 的路由信息,如果NameServer 全部挂掉,并且,此时依然可以发送消息。