rocketmq-基础概念
一、MQ概述
1、简介
MQ,是一种提供消费队列服务的中间件,是一套提供了消息生产、存储消费全过程API的系统。
2、MQ用途
限流削峰
MQ可以将系统的超量请求暂存其中,以便系统后期可以慢慢进行处理,从而避免请求的丢失或系统被压垮。
异步解耦
上游系统对下游系统的调用,则会大大降低系统的吞吐量与并发度,且系统耦合度太高。
而异步调用则会解决这些问题。因此两层之间若要实现由同步转异步的话,一般做法是,在两层之间加一层MQ层。
数据收集
分布式系统会产生海量级数据流,如:业务日志,监控数据,用户行为等。针对这些数据进行实时或批量汇总,然后对这些数据流进行大数据分析,可以通过MQ数据收集是最好的选择。
3、RocketMQ
RocketMQ是使用Java语言开发的MQ产品。经过数年阿里双11的考验,性能与稳定性非常高。其没有遵循任何常见的MQ协议,而是使用自研协议。
二、RocketMQ概述
1、简介
RocketMQ是一个统一消息引擎、轻量级数据处理平台。
RocketMQ是一款阿里巴巴开源的消息中间件。
官网:http://rocketmq.apache.org
2、基本概念
1、消息(message)
消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于一个主题(topic)。
2、主题(topic)
topic表示一类消息的集合,每个主题包含若干条消息,每条消息属于一个主题,是RocketMQ进行消息订阅的基本单位。
3、标签(tag)
用于同一个主题下区分不同类型的消息。来自同一业务单元的消息,可以根据业务的不同设置不同的标签。相当于对主题进行再次分类。
4、队列
存储消息的物理实体,一个Topic中可以包含多个Queue,每个Queue中存放的就是该Topic的消息。
一个Topic的Queue中的消息只能被一个消费组中的消费者消费。一个Queue中的消息不允许同一个消费者组的多个消费者同时消费。
在学习的参考资料中,还会看到一个概念,分片(Sharding)。在RocketMQ中,分片指的是存放相应的Topic的Broker。每个分片中会创建出相应数量的分区,即Queue。
如下图,一个分片在不同的Broker中,每个Broker的分片存在一个Topic,每个分片的Topic的Queue的数量不同,但Queue的大小相同。
5、消息标识(MessageId/Key)
RocketMQ中每个消息的唯一标识,且可以携带具有业务标识的Key,以便消息查询。
需要注意的是,MessageId有两个:在生产者send()消息时会自动生成一个MessageId(msgId),
当消息到达broker后,broker也会生成一个MessageId(offsetMsgId),msgId、offsetMsgId与key
都称为消息标识。
- msgId:由生产者端生成,其生成规则:
producerIp + 进程pid + MessageClientIdSetter类的ClassLoader的hashCode + 当前时间 + AutomicInteger自增计数器
- offsetMsgId:由Broker端生成,其生成规则:
brokerIp + 物理分区的offset(Queue中的偏移量)
- key:由用户指定的业务相关唯一标识
三、RocketMQ架构
1、生产者
生产者负责生产消息。Producer通过MQ的负载均衡模块选择相应的Broker集群队列进行消息投递,投递的过程支持快速失败并且低延迟。
RocketMQ中的消息生产者都是以生产者组(Producer Group)的形势出现。生产者组是同一类生产者的集合,这类生产者发送相同Topic类型的消息。
一个生产者组可以发送多个Topic的消息。
2、消费者
1)、消费者负责消费消息。Consumer会从Broker服务器中获取消息,并且对消息进行相关业务处理。
2)、RocketMQ中的消费者都是以消费者组的形势出现的。Consumer Group是同一类Consumer的集合,这类Consumer消费同一个Topic类型的消息。
3)、Consumer Group在消费方面实现了Loader Balance(将一个Topic的不同Queue平均分配给同一个Consumer Group的不同的Consumer)
和容错(一个Consumer挂了,该Consumer Group的其他Consumer可以接着消费原Consumer消费的Queue)的目标变得非常容易。
4)、消费者组的Consumer的数量应该 <= 订阅的Queue的数量。如果超出Queue的数量,那么多出来的Consumer将不能消费消息。
不过,一个Topic类型的消息可以被多个消费者组同时消费。
注意:第3点是消费者的核心。
3、NameServer
1、功能介绍
NameServer是一个Broker与Topic路由的注册中心,支持Broker的动态注册与发现。
以前的RocketMQ的注册中心依赖于Zookeeper,后来采用自己内部的NameServer来替代Zookeeper。
主要包括2个功能:
- Broker管理:接收Broker集群的注册信息并保存下来做为路由信息的基本数据;提供心跳检测机制,检查Broker是否存活。
- 路由信息管理:每个NameServer中都保存着Broker集群的整个路由信息和用于客户端查询的队列信息。Producer和Consumer通过NameServer可以获取整个Broker集群的路由信息,从而进行消息的投递和消费。
2、路由注册
1、NameServer通常也是集群部署,不过NameServer是无状态的,即NameServer集群中的各个节点之间是无差异的,各个节点相互不进行通讯。
2、那各个节点中的数据是怎么进行数据同步的?
在Broker节点启动时,轮询NameServer列表,与每个NameServer节点建立长连接,发起注册请求。中NameServer内部维护一个Broker列表,用来动态存储Broker信息。
3、Broker节点为了证明自己活着,会与NameServer维护长连接,将最新的信息以心跳包的方式上报给NameServer,每30秒发送一次心跳。心跳包:BrokerId、Broker地址、Broker名、Broker所属集群名。NameServer接收到心跳包后,更新心跳时间戳,记录这个Broker的存活时间。
3、路由剔除
由于Broker宕机、关机或网络抖动等原因,NameServer没有接收到Broker心跳,NameServer可能会将其Broker从列表中剔除。
NameServer中有一个定时任务,每隔10秒就会扫描一次Broker表,查看每个Broker的最新心跳时间戳距离当前时间是否超过120秒,
如果超过,则判定Broker失效,然后将其从Broker列表中剔除。
时间戳 - currentTime > 120,剔除其Broker。
4、路由发现
RocketMQ的路由发现采用的是Pull模型,当Topic路由信息发生变化时,NameServer不会主动推送给客户端,而是客户端定时Pull Topic最新路由。
默认客户端每30秒会Pull一次最新路由。
Client—30s—>拉取主题的最新路由;
扩展:
1)Push模型:推送模型。实时性较好,是一个发布-订阅模型,需要维护一个长连接。
适用场景:
实时性要求高;
Client数量不多,Server数据变化频繁;
2)Pull模型:拉取模型。实时性比较差。
3)Long Polling模型:长轮询模型。是对Push与Pull的整合,充分利用了这两种模型的优势,屏蔽了它们的劣势。
5、客户端与NameServer 连接策略
客户端在配置时,必须要写上NameServer集群地址,那么客户端到底连接的是哪个NameServer节点呢?
客户端首先会产生一个随机数,然后再与NameServer节点数量取模,此时得到的就是所要连接的节点索引,然后就会进行连接。
如果连接失败,则会采用轮询策略逐个尝试着去连接其他节点。
首先采用随机策略进行选择,失败后采用的是轮询策略。
4、Broker
功能介绍
Broker相当于消息的一个中转站,负责存储消息、转发消息。
Broker在RocketMQ中负责接收并存储来自Producer发送过来的消息,同时为Consumer的Pull请求做准备。
Broker同时存储着消息相关的元数据信息,包括 Consumer Group 消费进度偏移量offset、Topic、Queue等。
Broker集群部署
为了增强Broker性能与吞吐量,Broker一般都是以集群形势存在的。各集群节点中可能存放相同的Topic的不同的Queue。
但是会出现某个节点的Broker节点宕机,那么如何保证数据不丢失呢?
解决方案是,将每个Broker集群节点进行横向扩展,即将Broker节点再建为一个HA集群,解决单点问题。
Broker节点集群是一个主从集群,即Master与Slave。Master负责处理读写操作请求,Slave负责对Master中数据备份。
当Master挂了,Slave则会自动切换成Master去工作。一个Master可以包含多个Slave,一个Slave只属于一个Master。
Master与Slave通过指定相同的BrokerName、不同的BrokerId来确定的。BrokerId为0表示Master,非0表示Slave。
每个Broker与NameServer集群中的所有节点进行长连接,定时注册Topic信息到所有NameServer。
5、工作流程
具体流程
1)启动NameServer,NameServer启动后开启监听端口,等待Broker、Producer、Consumer连接。
2)启动Broker时,Broker会与所有的NameServer建立并保持长连接,然后每30秒向NameServer定时发送心跳包。
3)发送消息前,可以先创建Topic,创建Topic时需要指定该Topic要存储到哪些Broker上,当然,在创建Topic时,也会将Topic与Broker的关系写入到NameServer中。
不过这是可选的,也可以在发送消息的时候自动创建Topic。
4)Producer发送消息,启动时先跟NameServer集群中的其中一个节点建立长连接,并从NameServer中获取路由信息,即当前发送的Topic的Queue与Broker地址的映射关系。
根据算法从中选择一个Queue,与Queue所在的Broker建立长连接,从而向Broker发消息。
当然,在获取到路由信息后,Producer会首先将路由信息缓存到本地,再每30秒从NameServer更新一次路由信息。
5)Consumer跟Producer类似,跟其中一个NameServer建立长连接,获取其订阅Topic的路由信息,然后根据算法从路由信息获取到所要消费的Queue,
然后直接根据Broker建立长连接,开始消费message。Consumer在获取到路由信息后,同样也会每30秒向NameServer更新一次路由信息。
不过不同于Producer的是,Consumer还会向Broker发送心跳,以确保Broker的存活状态。
Topic创建模式
1)手动创建Topic:
集群模式:该模式下创建的Topic在该集群中,所有的Broker中的Queue数量是相同的。
Broker模式:该模式下创建的Topic在该集群中,每个Broker中的Queue数量可以不同。
2)自动创建Topic时,默认采取的是Broker模式,会为每个Broker默认创建4个Queue。
读写队列
从物理上来讲,读写队列是同一个队列。所以,不存在读写队列数据同步问题。读写队列从逻辑上进行分区的概念。一般情况,读写队列数量是相同的。
例如:创建Topic时,设置写队列数量为4,读队列数量为8,此时会创建8个Queue,分别是01234567。Producer会将message写入0123这4个队列,但
Consumer只会从01234567中消费,4567中是没有消息的。假设Consumer Group中包含两个Consumer,Consumer1消费0123,Consumer2消费4567。
但实际情况,Consumer2是没有消息可消费的。
这样设计的目的是方便Topic的Queue的缩容。
perm用于设置对当前创建Topic的操作权限:2表示只写,4表示只读,6表示读写。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!