1. 名词解释和概念
NameServer:
- 是一个无状态节点,可集群部署,节点之间无任何信息同步
- 用于服务注册和发现,为 MQ 集群提供服务协调与治理
- 记录并维护 Topic 和 Broker 的信息
- 为生产者和消费者提供 Topic 的路由信息
无状态和有状态:
举例:用户登录完后,将用户信息保存在一个地方,供以后其他动作来获取这些数据来完成对应的操作。这个时候,如果有动作需要获取这些保存的用户信息才能完成,那么我们就称这个动作是有状态的。用于存放用户信息的地方叫做暂存区,共用一个暂存区的动作,称之为“上下文”
现在我们给出无状态的概念:每次动作所需的数据全部由外界提供,服务端内部不做任何的暂存;并且请求可以提交到服务端的任意副本节点上,处理结果都是完全一样的
拓展:为什么现在都说要做成无状态的呢?
在现在的分布式系统中,“有状态”也就意味着用户的请求发送到服务端的时候,服务端里都保存着这个用户的一些基本信息;在微服务的架构中,用户的一些基本操作和主要功能的操作可能是分散在不同的服务器中进行,那么用户的信息都保存在用于处理用户基本操作的服务器中;如果此时这台服务器宕机了,那么该用户的一些操作将没有办法同步到备份的服务器中,除非这台服务器实时与主服务器保持用户信息的同步。所以现在大多数主流的观点都说要做成无状态的方式
在无状态的方式下下,用户的信息会随着每次的请求发送到服务器,专业一点来说,就是每次需要处理的数据都通过上游的服务器放到参数中传进来
当然,无状态方式下也会有属于他的弊端,就是每次传输的网络数据包会比较大
Broker:
- RocketMQ 的核心,负责消息的接收,存储,拉取等功能
- 分为 Master 和 Slave ,一个 Master 对应多个 Slave
- 每个 Broker 都会与 NameServer 集群中的每个节点保持长连接,定时把 Topic 的信息同步到 NameServer
- 在启动的时候,会首先向 NameServer 注册,然后同步 Topic 的路由信息
Producer:生产者;用来构建并传输消息到服务端的实体
Consumer:
- 消费者;用来接收并处理消息的实体
- Push Consumer:推消费者,会注册一个监听的接口,一旦监听到有消息,就会立马回调监听接口中的方法,让 broker 将消息推送过来
- Pull Consumer:拉消费者,消息的拉取由应用控制;当应用需要消息时,才会去 broker 拉取消息
Topic:主题
- 消息的第一级类型
- 是消息的逻辑分类,一个 Topic 可以分布在不同的 Broker 上,把一个 Topic 分布在一个 Broker 上的子体称为分片
- 数据分片其实是指按照某种规则将存放在一个地方的数据分散的存放在多个数据节点中,这样在高并发的场景下,请求被分散到各个节点中,从而达到提升性能的目的
Message Queue:
- Topic 被划分为一个或多个子主题,称为 Message Queue
- 一个 Topic 下,可以设置多个 Queue
Tags:是 Topic 下的第二级消息类型,可以使用 Tags 在同一个 Topic 下进行消息过滤
2. 架构图
3. 发送和消费流程
3.1 发送流程
注意:上面流程图中有一个寻找 “默认路由” 的操作
说明:如果一个消息中指定的 Topic ,没有找到对应的 Broker,那么这个时候会去寻找可以自动创建不存在 Topic 的 Broker,让这个 Broker 去创建不存在的 Topic ,然后再把消息发送至新的 Topic 中
但是这样会有一个弊端,设想:如果此时生产者在连续不断的发送消息,因为 Broker 并不是实时的把新的 Topic 路由信息同步给 NameServer 的,而是每隔一段时间同步一次。那么在没有同步的这段时间里,由于生产者还在不断发送消息,那么就有可能出现其他也可以自动创建新 Topic 的 Broker 也在自己那里创建该 Topic。这种情况是好的,这样当同步路由信息给 NameServer 时,下次就会有多个 Broker 去处理这个 Topic 的消息,达到负载均衡的效果。但是,如果生产者不是连续不断的发送,而是偶尔发一次,且间隔的时间大于每次 Broker 同步给 NameServer 的时间,那么在 NameServer 中只记录了只有这一个 Broker 可以处理该 Topic 下的消息,此时就达不到负载均衡的效果了
3.2 消费流程
3.2.1 消息获取
消费者消费消息有两种获取消息的方式:push 和 pull
push:推模式
pull:拉模式
顾名思义:推模式是 Broker 主动向 Consumer 推送消息;拉模式是 Consumer 根据需要,请求 Broker 将消息返回
实际上,push 和 pull 都是采用 Consumer 主动拉取的方式
push 模式下,其实是建立了一个长轮询的 pull 模式
两者的不同之处在于:push 模式下,如果 Broker 里没有数据,Broker 会阻塞该请求,直到有数据返回或者超过本次请求的时间才会返回;而 pull 模式下则不会对请求进行阻塞
3.2.2 消息消费
所谓消费:其实就是 Consumer 订阅关注的 Topic ,然后获取并消费消息
消费者通常是以一个集群的方式存在,相同 Group ID 的 Consumer 属于同一个集群,同一个集群下的 Consumer 的消费逻辑必须是一样的
消费模式分为两种:集群消费和广播消费
集群消费:任意一条消息只要被同一个集群下的 Consumer 消费过一次即可,集群下的 Consumer 平摊所有的消息,达到负载均衡的目的
广播消费:消息会推送给集群中所有的 Consumer,保证消息被每一个 Consumer 消费一次
4. 消息的种类
同步消息:指消息发送方发出数据后,会进入阻塞状态,直到 MQ 服务方发回响应消息
适用场景:重要通知消息的发送
异步消息:指发送方发出数据后,无需等接收方响应,继续发送下个数据包。在异步发送的模式下,用户端不需要等待服务器响应即可直接返回,通过回调接口接收服务器的响应结果并对其进行处理
适用场景:一般适用于处理的链路较长,对 RT时间 敏感的业务场景;比如大资料的上传等
单向消息:只负责发送消息,服务器的回应对其来说并不重要,只需保证消息发送出去了即可
适用场景:适用于耗时短,但对可靠性要求不高的场景
事务消息:RocketMQ提供分布式事务功能来保证业务员发送方和MQ消息的一致性,其本质是使用半消息(Prepared 消息和 Commit 消息)机制来实现
流程如下:
- 发送方向 RocketMQ 发送消息
- RocketMQ 将消息持久化之后,向发送方发送 ACK 信号,此时消息为 Prepared 消息
- 发送方开始执行本地事务逻辑,根据本地事务执行结果向 RocketMQ 提交二次确认(Commit 或是 Rollback),RocketMQ 收到 Commit 状态则将 Prepared 消息标记为可投递,订阅方最终将收到该消息;RocketMQ 收到 Rollback 状态则删除 Prepared 消息,订阅方将不会接受该消息
如果在上述流程的第2阶段,提交的二次确认最终未到达 RocketMQ,RocketMQ 会定时扫描消息集群中的事物消息,如果发现 Prepared 消息,会向消息发送者确认本地事务的最终执行结果;根据消息发送者的返回来决定是回滚消息还是继续发送确认消息,保证消息发送与本地事务的同时成功或失败
如果本地事务已经成功,且消息也发送成功,那么压力来到消费者这边
此时会有两个问题:消费失败和消费超时;消费超时的话,只需一直重试即可;消费失败的话,按照阿里提供的说法,需要“人工解决,RabbitMQ 并未提供回滚上述整个流程的机制