消息中间件,英文 Message Queue,简称 MQ。它没有标准定义,一般认为:消息中间件属于分布式系统中的一个子系统,关注于数据的发送和接收,利用高效可靠的异步消息传递机制对分布式系统中的其余各个子系统进行集成。
高效:对于消息的处理处理速度快,RocketMQ 可以达到单机 10 万+的并发。
可靠:一般消息中间件都会有消息持久化机制和其他的机制确保消息不丢失。
异步:指发送完一个请求,不需要等待返回,随时可以再发送下一个请求,既不需要等待。
一句话总结:消息中间件不是生产消息,只是消息的搬运工。
系统的耦合性越高,容错性就越低。以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。
使用消息中间件,系统的耦合性就会提高了。比如物流系统发生故障,需要几分钟才能修复,在这段时间内,物流系统要处理的数据被缓存到消息队列中,用户的下单操作正常完成。当物流系统恢复后,继续处理存放在消息队列中的订单消息即可,终端系统感知不到物流系统发生过几分钟故障。
流量削峰
应用系统如果遇到系统请求流量的瞬间猛增,有可能会将系统压垮。有了消息队列可以将大量请求缓存起来,分散到很长一段时间处理,这样可以大大提到系统的稳定性和用户体验。
互联网公司的大促场景(双十一、店庆活动、秒杀活动)都会使用到 MQ。
数据分发
通过消息队列可以让数据在多个系统之间进行流通。数据的产生方不需要关心谁来使用数据,只需要将数据发送到消息队列,数据使用方直接在消息队列中直接获取数据即可。
接口调用的弊端,无论是新增系统,还是移除系统,代码改造工作量都很大。
使用 MQ 做数据分发的好处,无论是新增系统,还是移除系统,代码改造工作量较小。所以使用MQ 做数据的分发,可以提高团队开发的效率。
NameServer
NameServer 是整个 RocketMQ 地球的“大脑”,它是 RocketMQ 因为服务注册中心,所以 RocketMQ需要先启动 NameServer 再启动 Rocket 中的 Broker。
Broker 在启动时向所有 NameServer 注册(主要是服务器地址等),生产者在发送消息之前先从 NameServer 获取 Broker 服务器地址列表(消费者一样),然后根据负载均衡算法从列表中选择一台服务器进行消息发送。
主机(Broker)
RocketMQ 的核心,用于暂存和传输消息。
生产者(Producer)
生产者:也称为消息发布者,负责生产并发送消息至 RocketMQ。
消费者(Consumer)
消费者:也称为消息订阅者,负责消息的发布 RocketMQ 接收并消费消息。
消息(Message)
消息:生产或消费的数据,对于 RocketMQ 来说,消息就是字节数组。
主题(Topic)
标识 RocketMQ 中一类消息的逻辑名字,消息的逻辑管理单位。无论是生产还是消费,都需要指定 Topic。主题主要用于区分消息的种类:一个生产者可以发送消息给一个或者多个 Topic,消息的消费者也可以订阅一个或者多个 Topic 消息
消息队列(Message Queue)
简称 Queue 或 Q。消息物理管理单位。一个 Topic 将有若干个 Q。
无论生产者还是消费者,实际的生产和消费都是针对 Q 级别。例如 Producer 发送消息的时候,会预先选择(默认轮询)还是该 Topic 下面的某一条 Q 发送;Consumer 消费的时候也会负载均匀衡地分配若干个 Q,只拉取对应 Q 的消息。
若一个 Topic 创建在不同的 Broker,则不同的 broker 上都有若干 Q,消息将物理地存储落在不同 Broker 结点上,具有水平扩展的能力。
分组(Group)
生产者:表示发送同一类消息的 Producer,通常发送逻辑一致。发送普通消息的时候,仅标注识使用,并无特别用处。主要作用是用于事务消息:
消费者:标识一类 Consumer 的集合名称,这类Consumer 通常消费一类消息(也称为 ConsumerGroup),且消费逻辑一致。同一个 Consumer Group 下的各个实例将共同消费 topic 的消息,起到负载均衡的作用。
标签(Tag)
RocketMQ 支持给在发送的时候给消息打 tag,同一个 topic 的消息虽然逻辑管理是一样的。但是消费同一个 topic 时,如果你消费订阅的时候指定的是 tagA,那么 tagB 的消息将不会投递。
偏移量(Offset)
RocketMQ 中,有很多 offset 的概念。一般我们只关心暴露到客户端的 offset。不指定的话,就是指 Message Queue 下面的 offset。
Message queue 是无限长的数组。一条消息进来下标就会涨 1,而这个数组的下标就是 offset,Message queue 中的 max offset 表示消息的最大 offset
Consumer offset 可以理解为标记 Consumer Group 在一条逻辑 Message Queue 上,消息消费到哪里即消费进度。但从源码上看,这个数值是消费过的最新消费的消息 offset+1,即实际上表示的是下次拉取的 offset 位置。
普通消息
本章节先会使用 RocketMQ 提供的原生客户端的 API,当然除了原生客户端外,SpringBoot、SpringCloudStream 也进行了集成,但本质上这些也是基于原生 API 的封装,所以只需掌握原生 API,其他的也会水到渠成。
Java 代码中使用普通消息的整体流程如下
导入 MQ 客户端依赖
消息发送者步骤
消息消费者步骤
发送同步消息
同步发送是指消息发送方发出数据后,同步等待,直到收到接收方发回响应之后才发下一个请求。这种可靠性同步地发送方式使用的比较广泛,比如:重要的消息通知,短信通知。
代码演示
发送结果分析
消息的全局唯一标识(RocketMQ 的 ID 生成是使用机器 IP 和消息偏移量的组成),由消息队列 MQ 系统自动生成,唯一标识某条消息。
发送的标识:成功,失败等
queueId 是 Topic 的分区;Producer 发送具体一条消息时,对应选择的该 Topic 下的某一个 Queue 的标识 ID。
Message queue 是无限长的数组。一条消息进来下标就会涨 1,而这个数组的下标就是queueOffset,queueOffset 是从 0 开始递增。
发送异步消息
异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间的等待 Broker 的响应。消息发送方在发送了一条消息后,不等接收方发回响应,接着进行第二条消息发送。发送方通过回调接口的方式接收服务器响应,并对响应结果进行处理。
代码演示
发送结果分析跟发送同步消息相同。
单向发送
这种方式主要用在不特别关心发送结果的场景,例如日志发送。单向(Oneway)发送特点为发送方只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答。此方式发送消息的过程耗时非常短,一般在微秒级别。
代码演示
消息发送的权衡
负载均衡模式(集群消费)
消费者采用负载均衡方式消费消息,一个分组(Group)下的多个消费者共同消费队列消息,每个消费者处理的消息不同。一个 Consumer Group 中的各个 Consumer 实例分摊去消费消息,即一条消息只会投递到一个 Consumer Group 下面的一个实例。例如某个 Topic 有 3 个队列,其中一个Consumer Group 有 3 个实例,那么每个实例只消费其中的 1 个队列。集群消费模式是消费者默认的消费方式。
代码演示
广播消费
广播消费模式中消息将对应一个 Consumer Group 下的各个 Consumer 实例都投递一遍。即使这些 Consumer 属于同一个 Consumer Group,消息也会被 Consumer Group 中的每个 Consumer 都消费一次。实际上,是一个消费组下的每个消费者实例都获取到了 topic 下面的每个 MessageQueue 去拉取消费。所以消息会投递到每个消费者手里。
代码演示