RocketMq
2023-08-30 16:59:25 0 举报
RocketMQ底层原理
作者其他创作
大纲/内容
消息模式
点对点
消息生产者向消息队列中发送了一个消息之后,只能被一个消费者消费一次。
发布/订阅
消息生产者向频道发送一个消息之后,多个消费者可以从该频道订阅到这条消息并消费
观察者模式中,观察者和主题都知道对方的存在;而在发布与订阅模式中,生产者与消费者不知道对方的存在,它们之间通过频道进行通信。
观察者模式是同步的,当事件触发时,主题会调用观察者的方法,然后等待方法返回;
而发布与订阅模式是异步的,生产者向频道发送一个消息之后,就不需要关心消费者何时去订阅这个消息,可以立即返回。
使用场景
异步处理
发送者将消息发送给消息队列之后
不需要同步等待消息接收者处理完毕
而是立即返回进行其它操作
消息接收者从消息队列中订阅消息之后异步处理
流量削锋
在高并发的场景下,如果短时间有大量的请求到达会压垮服务器
可以将请求发送到消息队列中,服务器按照其处理能力从消息队列中订阅消息进行处理
应用解耦
模块之间耦合度就会很低,新增或修改模块影响很小,从而实现可扩展性
可靠性
发送端的可靠性
发送端完成操作后一定能将消息成功发送到消息队列中
实现方式,消息记录,通过本地事务控制,失败重试
接收端的可靠性
接收端能够从消息队列成功消费一次消息
实现方式,消息幂等校验,消息ID唯一校验
MQ
简介
Message queue,消息队列,就是指保存消息的一个容器
常用组件
ActiveMQ
RabbitMQ
RocketMQ
ZeroMQ
MetaMQ
kafka
特点
先进先出
发布订阅
持久化
分布式
消息中间件对比
Kafka
Apache开源分布式发布-订阅消息系统
Kafka主要特点是基于Pull的模式来处理消息消费,,追求高吞吐量
一开始的目的就是用于日志收集和传输
0.8版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求
适合产生大量数据的互联网服务的数据收集业务
并发量
Kafka的吞吐量高达17.3w/s
它的队列模式保证了写磁盘的过程是线性IO
主要用于处理活跃的流式数据,大数据量的数据处理上,常用日志采集,数据采集上
RabbitMQ
Erlang语言开发的开源消息队列系统
基于AMQP协议来实现
面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全
AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次
并发量
RabbitMQ的吞吐量5.95w/s,CPU资源消耗较高
它支持AMQP协议,实现非常重量级,为了保证消息的可靠性在吞吐量上做了取舍
用在实时的对可靠性要求比较高的消息传递上,适合企业级的消息发送订阅
RocketMQ
阿里开源的消息中间件,纯Java开发
具有高吞吐量、高可用性、适合大规模分布式系统应用的特点
RocketMQ思路起源于Kafka,但并不是Kafka的一个Copy,它对消息的可靠传输及事务性做了优化
目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景
对路由(Routing),负载均衡(Load balance)或者数据持久化都有很好的支持
并发量
吞吐量在11.6w/s,磁盘IO %util已接近100%
RocketMQ的消息写入内存后即返回ack,由单独的线程专门做刷盘的操作,所有的消息均是顺序写文件
Redis
可以当做一个轻量级的队列服务来使用
入队时,当数据比较小时Redis的性能要高于RabbitMQ
而如果数据大小超过了10K,性能极大降低
出队时,无论数据大小,Redis都表现出非常好的性能
ZeroMQ
ZeroMQ具有一个独特的非中间件的模式
只需要简单的引用ZeroMQ程序库,即可应用程序之间发送消息
非持久性的队列,宕机会丢失消息
ActiveMQ
支持许多跨语言客户端和协议,带有易于使用的企业集成模式和许多高级功能
异步调用,一对多通信,做多个系统的集成,同构、异构,作为RPC的替代,多个应用相互解耦,作为事件驱动架构的幕后支撑,为了提高系统的可伸缩性
RocketMQ
简介
是一款低延迟、高可靠、可伸缩、易于使用的消息中间件
关键字
Producer
消息生产者,生产者的作用就是将消息发送到 MQ
Producer Group
生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组
Consumer
消息消费者
Consumer Group
消费同一类消息的多个 consumer 实例组成一个消费者组
Topic
Topic 是一种消息的逻辑分类
Message
是消息的载体
Tag
标签,可根据业务细分
Broker
Broker 是 RocketMQ 系统的主要角色
Broker 接收来自生产者的消息
储存以及为消费者拉取消息的请求做好准备
Name Server
为 producer 和 consumer 提供路由信息
RocketMQ 架构
图示
流程
集群模式
广播模式
offset
四个集群
NameServer 集群
提供轻量级的服务发现和路由
每个 NameServer 记录完整的路由信息
提供等效的读写服务
并支持快速存储扩展
Broker 集群
通过提供轻量级的 Topic 和 Queue 机制来处理消息存储
同时支持推(push)和拉(pull)模式
主从结构的容错机制
Producer 集群
Consumer 集群
流程
Producer 与 NameServer 集群中的其中一个节点(随机选择)建立长连接
定期从 NameServer 获取 Topic 路由信息
并向提供 Topic 服务的 Broker Master 建立长连接
且定时向 Broker 发送心跳
Producer 只能将消息发送到 Broker master
但是 Consumer 则不一样,它同时和提供 Topic 服务的 Master 和 Slave建立连接
既可以从 Broker Master 订阅消息,也可以从 Broker Slave 订阅消息
RocketMQ 集群部署模式
单 master 模式
多 master 模式
单个 master 节点宕机期间,未被消费的消息在节点恢复之前不可用,消息的实时性就受到影响。
多 master 多 slave 异步复制模式
master,节点可读可写
slave 只能读不能写
在 master 宕机时,消费者可以从 slave 读取消息,消息的实时性不会受影响,性能几乎和多 master 一样
使用异步复制的同步方式有可能会有消息丢失的问题
多 master 多 slave 同步双写模式
同步双写的同步模式能保证数据不丢失
发送单个消息 RT 会略长,性能相比异步复制低10%左右
刷盘策略:同步刷盘和异步刷盘(指的是节点自身数据是同步还是异步存储)
同步方式:同步双写和异步复制(指的一组 master 和 slave 之间数据的同步)
功能特性
普通消息
场景
普通消息一般应用于微服务解耦、事件驱动、数据集成等场景
这些场景大多数要求数据传输通道具有可靠传输的能力
且对消息的处理时机、处理顺序没有特别要求
功能原理
普通消息是Apache RocketMQ基本消息功能,支持生产者和消费者的异步解耦通信
普通消息生命周期
初始化:消息被生产者构建并完成初始化,待发送到服务端的状态
待消费:消息被发送到服务端,对消费者可见,等待消费者消费的状态
消费中:消息被消费者获取,并按照消费者本地的业务逻辑进行处理的过程
此时服务端会等待消费者完成消费并提交消费结果,如果一定时间后没有收到消费者的响应,Apache RocketMQ会对消息进行重试处理
消费提交:消费者完成消费处理,并向服务端提交消费结果,服务端标记当前消息已经被处理(包括消费成功和失败)
Apache RocketMQ默认支持保留所有消息,此时消息数据并不会立即被删除,只是逻辑标记已消费。
消息在保存时间到期或存储空间不足被删除前,消费者仍然可以回溯消息重新消费
消息删除
Apache RocketMQ按照消息保存机制滚动清理最早的消息数据,将消息从物理文件中删除
使用限制
普通消息仅支持使用MessageType为Normal主题
即普通消息只能发送至类型为普通消息的主题中,发送的消息的类型必须和主题的类型一致
使用建议
设置全局唯一业务索引键,方便问题追踪
RocketMQ支持自定义索引键(消息的Key)
定时消息
应用场景
定时消息和延时消息本质相同,都是服务端根据消息设置的定时时间在某一固定时刻将消息投递给消费者消费
在分布式定时调度触发、任务超时处理等场景,需要实现精准、可靠的定时事件触发
使用RocketMQ 的定时消息可以简化定时调度任务的开发逻辑,实现高性能、可扩展、高可靠的定时触发能力
精度高、开发门槛低:基于消息通知方式不存在定时阶梯间隔。可以轻松实现任意精度事件触发,无需业务去重
高性能可扩展:RocketMQ 的定时消息具有高并发和水平扩展的能力
功能原理
定时消息是 Apache RocketMQ 提供的一种高级消息类型,消息被发送至服务端后,在指定时间后才能被消费者消费
通过设置一定的定时时间可以实现分布式场景的延时调度触发效果
定时时间设置原则
设置的定时时间是一个预期触发的系统时间戳
延时时间也需要转换成当前系统时间后的某一个时间戳,而不是一段延时时长
定时时间的格式为毫秒级的Unix时间戳
定时时间必须设置在定时时长范围内,超过范围则定时不生效,服务端会立即投递消息
定时时长最大值默认为24小时,不支持自定义修改
定时时间必须设置为当前时间之后,若设置到当前时间之前,则定时不生效,服务端会立即投递消息
定时消息生命周期
初始化:消息被生产者构建并完成初始化,待发送到服务端的状态。
定时中:消息被发送到服务端,和普通消息不同的是,服务端不会直接构建消息索引,而是会将定时消息单独存储在定时存储系统中,等待定时时刻到达
待消费:定时时刻到达后,服务端将消息重新写入普通存储引擎,对下游消费者可见,等待消费者消费的状态
消费中:消息被消费者获取,并按照消费者本地的业务逻辑进行处理的过程。 等待消费结果,超时重试
消费提交:消费者完成消费处理,并向服务端提交消费结果,服务端标记当前消息已经被处理(包括消费成功和失败)
消费提交:默认支持保留所有消息
消息删除:Apache RocketMQ按照消息保存机制滚动清理最早的消息数据
使用限制
定时消息仅支持在 MessageType为Delay 的主题内使用
RocketMQ 定时消息的定时时长参数精确到毫秒级,但是默认精度为1000ms
定时消息的状态支持持久化存储,系统由于故障重启后,仍支持按照原来设置的定时时间触发消息投递
若存储系统异常重启,可能会导致定时消息投递出现一定延迟
使用建议
避免大量相同定时时刻的消息
顺序消息
应用场景
在有序事件处理、撮合交易、数据实时增量同步等场景下,异构系统间需要维持强一致的状态同步
上游的事件变更需要按照顺序传递到下游进行处理
顺序消息可以有效保证数据传输的顺序性
功能原理
支持消费者按照发送消息的先后顺序获取消息,从而实现业务场景中的顺序处理
在发送、存储和投递的处理过程中,更多强调多条消息间的先后顺序关系
顺序消息的顺序关系通过消息组(MessageGroup)判定和识别
发送顺序消息时需要为每条消息设置归属的消息组
相同消息组的多条消息之间遵循先进先出的顺序关系
不同消息组、无消息组的消息之间不涉及顺序性
基于消息组的顺序判定逻辑,支持按照业务逻辑做细粒度拆分
可以在满足业务局部顺序的前提下提高系统的并行度和吞吐能力
如何保证消息的顺序性
生产顺序
通过生产者和服务端的协议保障单个生产者串行地发送消息,并按序存储和持久化
单一生产者:消息生产的顺序性仅支持单一生产者,不同生产者分布在不同的系统,即使设置相同的消息组,不同生产者之间产生的消息也无法判定其先后顺序
串行发送:RocketMQ 生产者客户端支持多线程安全访问,但如果生产者使用多线程并行发送,则不同线程间产生的消息将无法判定其先后顺序
相同消息组的消息按照先后顺序被存储在同一个队列,不同消息组的消息可以混合在同一个队列中,且不保证连续
消费顺序
通过消费者和服务端的协议保障消息消费严格按照存储的先后顺序来处理
投递顺序
有限重试
顺序消息投递仅在重试次数限定范围内
生产顺序性和消费顺序性组合
消息需要严格按照先进先出(FIFO)的原则处理,即先发送的先消费、后发送的后消费
则必须要同时满足生产顺序性和消费顺序性
要求
设置消息组,保证消息顺序发送。顺序消费,按照消息组粒度,严格保证消息顺序。 同一消息组内的消息的消费顺序和发送顺序完全一致。
设置消息组,保证消息顺序发送,并发消费,尽可能按时间顺序处理。
未设置消息组,消息乱序发送。顺序消费,按队列存储粒度,严格顺序
未设置消息组,消息乱序发送。并发消费,并发消费,尽可能按照时间顺序处理
使用限制
顺序消息仅支持使用MessageType为FIFO的主题
即顺序消息只能发送至类型为顺序消息的主题中
使用建议
串行消费,避免批量消费导致乱序
消息组尽可能打散,避免集中导致热点
事务消息
应用场景
分布式事务的诉求
传统XA事务方案:性能不足,多分支环境下资源锁定范围大,并发度低,随着下游分支的增加,系统性能会越来越差
基于普通消息方案:一致性保障困难
ARocketMQ分布式事务消息,支持最终一致性
支持二阶段的提交能力。将二阶段提交和本地事务绑定,实现全局提交结果的一致性
功能原理
支持在分布式场景下保障消息生产和本地事务的最终一致性
事务消息处理流程
生产者将消息发送至Apache RocketMQ服务端
RocketMQ服务端将消息持久化成功之后,向生产者返回Ack确认消息已经发送成功,此时消息被标记为"暂不能投递",这种状态下的消息即为半事务消息
生产者开始执行本地事务逻辑
生产者根据本地事务执行结果向服务端提交二次确认结果
二次确认结果为Commit
服务端将半事务消息标记为可投递,并投递给消费者
二次确认结果为Rollback
服务端将回滚事务,不会将半事务消息投递给消费者
超时未响应
经过固定时间后,服务端将对消息生产者即生产者集群中任一生产者实例发起消息回查
生产者收到消息回查后,需要检查对应消息的本地事务执行的最终结果
生产者根据检查到的本地事务的最终状态再次提交二次确认
使用限制
消息类型一致性 MessageType 为 Transaction
消费事务性
事务消息保证本地主分支事务和下游消息发送事务的一致性
但不保证消息消费结果和上游事务的一致性
建议消费端做好消费重试,如果有短暂失败可以利用重试机制保证最终处理成功
中间状态可见性
事务消息为最终一致性,即在消息提交到下游消费端处理完成之前,下游分支和上游事务之间的状态会不一致
事务超时机制
事务消息的命周期存在超时机制,即半事务消息被生产者发送服务端后
指定时间内服务端无法确认提交或者回滚状态,则消息默认会被回滚
使用建议
生产者应该尽量避免本地事务返回未知结果
大量的事务检查会导致系统性能受损,容易导致事务处理延迟
消息回查时,对于正在进行中的事务不要返回Rollback或Commit结果,应继续保持Unknown的状态,事务执行较慢,消息回查太快
消息发送重试和流控机制
重试基本概念
因为网络故障、服务异常等原因导致调用失败
SDK中内置请求重试逻辑,尝试通过重试发送达到最终调用成功的效果
重试触发条件
调用超时.失败
网络异常
重启或下线
服务端错误码
系统逻辑错误:因运行逻辑不正确造成的错误
系统流控错误:因容量超限造成的流控错误
重试流程
生产者在初始化时设置消息发送最大重试次数
触发发后,按照设置的重试次数重试
到消息发送成功或达到最大重试次数重试结束
并在最后一次重试失败后返回调用错误响应
重试间隔
除服务端返回系统流控错误场景,会立即重试
控错误触发重试,按照指数退避策略进行延迟重试
功能约束
链路耗时阻塞评估
最终异常兜底
消息重复问题
消息流控机制
系统容量或水位过高
通过快速失败返回流控错误来避免底层资源承受过高压力
存储压力大,服务端请求任务排队溢出,消费能力不足
消费者分类
图示
关键词
PushConsumer
消费消息仅通过消费监听器处理业务并返回消费结果。消息的获取、消费状态提交以及消费重试都通过 Apache RocketMQ 的客户端SDK完成
消息的实时处理能力是基于SDK内部的典型Reactor线程模型实现的
SDK内置了一个长轮询线程,先将消息异步拉取到SDK内置的缓存队列中
再分别提交到消费线程中,触发监听器执行本地消费逻辑
客户端SDK和消费逻辑的唯一边界是消费监听器接口
客户端SDK严格按照监听器的返回结果判断消息是否消费成功,并做可靠性重试
所有消息必须以同步方式进行消费处理,并在监听器接口结束时返回调用结果,不允许再做异步化分发
适用场景
消息处理时间可预估
无异步化、高级定制场景
SimpleConsumer
SimpleConsumer 是一种接口原子型的消费者类型,消息的获取、消费状态提交以及消费重试都是通过消费者业务逻辑主动发起调用完成
图示
SimpleConsumer消费者类型中,客户端SDK和服务端通过ReceiveMessage和AckMessage接口通信
客户端SDK如果处理消息成功则调用AckMessage接口
如果处理失败只需要不回复ACK响应,即可在定义的消费不可见时间到达后触发消费重试流程
适用场景
提供原子接口,用于消息获取和提交消费结果,相对于PushConsumer方式更加灵活
消息处理时长不可控
需要异步化、批量消费等高级定制场景
需要自定义消费速率
消息过滤
应用场景
会将该主题中的所有消息投递给消费者。若消费者只需要关注部分消息
可通过设置过滤条件在 Apache RocketMQ 服务端进行过滤
只获取到需要关注的消息子集,避免接收到大量无效的消息
要解决的单个业务域即同一个主题内不同消息子集的过滤问题
功能概述
消息过滤定义
过滤的含义指的是将符合条件的消息投递给消费者,而不是将匹配到的消息过滤掉
消息过滤功能通过生产者和消费者对消息的属性、标签进行定义
服务端根据过滤条件进行筛选匹配,将符合条件的消息投递给消费者进行消费
消息过滤主要通过以下几个关键流程实现
生产者:生产者在初始化消息时预先为消息设置一些属性和标签,用于后续消费时指定过滤目标
消费者:消费者在初始化及后续消费流程中通过调用订阅关系注册接口,向服务端上报需要订阅指定主题的哪些消息,即过滤条件。
服务端:消费者获取消息时会触发服务端的动态过滤计算
服务端根据消费者上报的过滤条件的表达式进行匹配,并将符合条件的消息投递给消费者
订阅关系一致性
过滤表达式属于订阅关系的一部分
同一消费者分组内的多个消费者的订阅关系包括过滤表达式,必须保持一致
`否则可能会导致部分消息消费不到
Tag标签过滤
多Tag匹配:多个Tag之间为或的关系,不同Tag间使用两个竖线(||)隔开
全部匹配:使用星号(*)作为全匹配表达式
SQL属性过滤使用SQL92语法作为过滤规则表达式
消费者负载均衡
广播消费
各消费者分组都订阅相同的消息
以此实现单客户端级别的广播一对多推送效果
网关推送、配置推送等场景
共享消费
这些消费者共同分担消费者分组内的所有消息
实现消费者分组内流量的水平拆分和均衡负载
消费者负载均衡策略
消息粒度负载均衡
PushConsumer和SimpleConsumer默认负载策略
照消息粒度平均分摊主题中的所有消息
即同一个队列中的消息,可被平均分配给多个消费者共同消费
顺序消息负载机制
息粒度负载均衡策略还需要保证同一消息组内的消息
按照服务端存储的先后顺序进行消费
不同消费者处理同一个消息组内的消息时
会严格按照先后顺序锁定消息状态,确保同一消息组的消息串行消费
策略特点
消费分摊更均衡
对非对等消费者更友好
适用场景
消息粒度消费负载均衡策略下
同一队列内的消息离散地分布于多个消费者,适用于绝大多数在线事件处理的场景
队列粒度负载均衡
PullConsumer默认负载策略
同一消费者分组内的多个消费者将按照队列粒度消费消息,即每个队列仅被一个消费者消费
基于队列数量、消费者数量等运行数据进行统一的算法分配
将每个队列绑定到特定的消费者按照
取消息>提交消费位点>持久化消费位点的消费语义处理消息
相对于消息粒度负载均衡策略,队列粒度负载均衡策略分配粒度较大
不够灵活。但该策略在流式处理场景下有天然优势
能够保证同一队列的消息被相同的消费者处理
对于批量处理、聚合处理更友好
队列粒度负载均衡策略适用于流式计算、数据聚合等需要明确对消息进行聚合、批处理的场景
消费进度管理
消费进度原理
消息位点(Offset)
主题和队列的定义,消息是按到达服务端的先后顺序存储在指定主题的多个队列中
每条消息在队列中都有一个唯一的Long类型坐标,这个坐标被定义为消息位点
任意一个消息队列在逻辑上都是无限存储,即消息位点会从0到Long.MAX无限增加
通过主题、队列和位点就可以定位任意一条消息的位置
队列中最早一条消息的位点为最小消息位点(MinOffset)
最新一条消息的位点为最大消息位点(MaxOffset)
会滚动删除队列中存储最早的消息,消息的最小消费位点和最大消费位点会一直递增变化
消费位点(ConsumerOffset)
通过消费位点管理消息的消费进度
每条消息被某个消费者消费完成后不会立即在队列中删除
会基于每个消费者分组维护一份消费记录
该记录指定消费者分组消费某一个队列时
消费过的最新一条消息的位点,即消费位点
重启时,会严格按照服务端保存的消费进度继续处理消息
服务端保存的历史位点信息已过期被删除,此时消费位点向前移动至服务端存储的最小位点
消费位点初始值
消费位点初始值指的是消费者分组首次启动消费者消费消息时,服务端保存的消费位点的初始值
定义消费位点的初始值为消费者首次获取消息时,该时刻队列中的最大消息位点
相当于消费者将从队列中最新的消息开始消费
重置消费位点
初始消费位点不符合需求,需要消费部分历史消息,指定历史时刻点位
消费堆积快速清理,更新到指定位置,绕过堆积的消息
业务回溯,指定历史消息,重新处理
消费重试
重试过程状态机:控制消息在重试流程中的状态和变化逻辑。
重试间隔:上一次消费失败或超时后,下次重新尝试消费的间隔时间。
最大重试次数:消息可被重试消费的最大次数。
MQ状态
Ready:已就绪状态
Inflight:处理中状态
WaitingRetry:待重试状态
Commit:提交状态
DLQ:死信状态 超过重试次数,会被投递至死信队列,可以通过消费死信队列的消息进行业务恢复
消息存储和清理机制
使用存储时长作为消息存储的依据,即每个节点对外承诺消息的存储时长
在存储时长范围内的消息都会被保留,无论消息是否被消费;超过时长限制的消息则会被清理掉
消息存储机制主要定义以下关键问题
消息存储管理粒度:按存储节点管理消息的存储时长,并不是按照主题或队列粒度来管理
消息存储判断依据:消息存储按照存储时间作为判断依据,相对于消息数量、消息大小等条件,使用存储时间作为判断依据,更利于业务方对消息数据的价值进行评估
消息存储和是否消费状态无关:消息存储是按照消息的生产时间计算,和消息是否被消费无关。按照统一的计算策略可以有效地简化存储机制
消息存储管理粒度说明
消息存储优势权衡
基于统一的物理日志队列和轻量化逻辑队列的二级组织方式,管理物理数据
安全生产和容量保障风险要求
最合理的方式是将不同存储时长的消息通过不同集群进行分离治理
消息保存时长并不能完整控制消息的实际保存时间
因为消息存储仍然使用本地磁盘,本地磁盘空间不足时
为保证服务稳定性消息仍然会被强制清理,导致消息的实际保存时长小于设置的保存时长
消息存储时长建议适当增加,可以为紧急故障恢复、应急问题排查和消息回溯带来更多的可操作空间
0 条评论
下一页