RocketMQ

RocketMQ概述

MQ,Message Queue,是一种提供消息队列服务的中间件,也称为消息中间件。消息即数据,一般消息的体量不会很大。简单来说,消息队列就是一种实现“先进先出”的一种数据结构。

用途

  1. 应用解藕:上游系统对下游系统的调用若为同步调用,则会大大降低系统的吞吐量与并发度,且系统耦合度太高。 而异步调用则会解决这些问题。所以两层之间若要实现由同步到异步的转化,一般性做法就是,在这两 层间添加一个MQ层。
  2. 流量消峰:MQ可以将系统的 超量 请求暂存其中,以便系统后期可以慢慢进行处理,从而避免了请求的丢失或系统被压垮。
  3. 消息分发:数据的生产方只需要把各自的数据写入一个消息队列即可,数据使用方根据各自需求订阅感兴趣的数据,不同团队所订阅的数据互不干扰,也不必和数据产生方关联。

RocketMQ的架构

RocketMQ 技术架构中有四大角色 NameServer 、Broker 、Producer 、Consumer:

  • Broker: 主要负责消息的存储、投递和查询以及服务高可用保证。说白了就是消息队列服务器,生产者生产消息到 Broker ,消费者从 Broker 拉取消息并消费。一个 Topic 分布在多个 Broker上,一个 Broker 可以配置多个 Topic ,它们是多对多的关系。 如果某个 Topic 消息量很大,应该给它多配置几个队列(上文中提到了提高并发能力),并且 尽量多分布在不同 Broker 上,以减轻某个 Broker 的压力 。
  • NameServer:Broker会将自己的信息注册到 NameServer 中,此时 NameServer 就存放了很多 Broker 的信息(Broker的路由表),消费者和生产者就从 NameServer 中获取路由表然后照着路由表的信息和对应的 Broker 进行通信(生产者和消费者定期会向 NameServer 去查询相关的 Broker 的信息)。
  • Producer: 消息发布的角色,支持分布式集群方式部署。
  • Consumer: 消息消费的角色,支持分布式集群方式部署。支持以push推,pull拉两种模式对消息进行消费。同时也支持集群方式和广播方式的消费,它提供实时消息订阅机制。

基本概念

消息(Message)

消息是指,消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于一个主题。

主题(Topic)

Topic表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是RocketMQ进行消息订阅的基本单位。

一个生产者可以同时发送多种Topic的消息;而一个消费者只对某种特定的Topic感兴趣,即只可以订阅和消费一种Topic的消息。

每个主题中都有多个队列(分布在不同的Broker中,如果是集群的话,Broker又分布在不同的服务器中),集群消费模式下,一个消费者集群多台机器共同消费一个 topic 的多个队列,一个队列只会被一个消费者消费。如果某个消费者挂掉,分组内其它消费者会接替挂掉的消费者继续消费。

标签(Tag)

为消息设置的标签,用于同一主题下区分不同类型的消息。来自同一业务单元的消息,可以根据不同业 务目的在同一主题下设置不同标签。标签能够有效地保持代码的清晰度和连贯性,并优化RocketMQ提 供的查询系统。消费者可以根据Tag实现对不同子主题的不同消费逻辑,实现更好的扩展性。

Topic是消息的一级分类,Tag是消息的二级分类。

队列(Queue)

存储消息的物理实体。一个Topic中可以包含多个Queue,每个Queue中存放的就是该Topic的消息。

一个Topic的Queue也被称为一个Topic中消息的分区(Partition)。 一个Topic的Queue中的消息只能被一个消费者组中的一个消费者消费。一个Queue中的消息不允许同一个消费者组中的多个消费者同时消费。

消息标识(MessageId/Key)

RocketMQ中每个消息拥有唯一的MessageId,且可以携带具有业务标识的Key,以方便对消息的查询。 不过需要注意的是,MessageId有两个:在生产者send()消息时会自动生成一个MessageId(msgId), 当消息到达Broker后,Broker也会自动生成一个MessageId(offsetMsgId)。msgId、offsetMsgId与key都称为消息标识。

消费位移(offset)

在发布订阅模式中一般会涉及到多个消费者组,而每个消费者组在每个队列中的消费位置都是不同的。如果此时有多个消费者组,那么消息被一个消费者组消费完之后是不会删除的(因为其它消费者组也需要呀),它仅仅是为每个消费者组维护一个 消费位移(offset) ,每次消费者组消费完会返回一个成功的响应,然后队列再把维护的消费位移加一,这样就不会出现刚刚消费过的消息再一次被消费了。

消息分类

消息发送分类

  1. 同步发送消息:Producer发出一条消息后,会在收到MQ返回的ACK之后才发下一条消息。该方式的消息可靠性最高,但消息发送效率太低。
  2. 异步发送消息:Producer发出消息后无需等待MQ返回ACK,直接发送下一条消息。该方式的消息可靠性可以得到保障,消息发送效率也可以。
  3. 单向发送消息:roducer仅负责发送消息,不等待、不处理MQ的ACK。该发送方式时MQ也不返回ACK。该方式的消息发送效率最高,但消息可靠性较差。

顺序消息

顺序消息指的是,严格按照消息的发送顺序进行消费的消息(FIFO)。

默认情况下生产者会把消息以Round Robin轮询方式发送到不同的Queue分区队列;而消费消息时会从 多个Queue上拉取消息,这种情况下的发送和消费是不能保证顺序的。如果将消息仅发送到同一个 Queue中,消费时也只从这个Queue上拉取消息,就严格保证了消息的顺序性。


根据有序范围的不同,RocketMQ可以严格地保证两种消息的有序性:分区有序与全局有序。

  • 当发送和消费参与的Queue只有一个时所保证的有序是整个Topic中消息的顺序,称为全局有序。
  • 如果有多个Queue参与,其仅可保证在该Queue分区队列上的消息顺序,则称为分区有序。

延时消息

当消息写入到Broker后,在指定的时长后才可被消费处理的消息,称为延时消息。

采用RocketMQ的延时消息可以实现 定时任务 的功能,而无需使用定时器。典型的应用场景是,电商交易中超时未支付关闭订单的场景,12306平台订票超时未支付取消订票的场景。

事务消息

消费问题

顺序消费

RocketMQ 在主题上是无序的、它只有在队列层面才是保证有序的。将同一语义下的消息放入同一个队列,使用 Hash取模法 来保证同一个订单在同一个队列中。

重复消费

幂等,在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。
可以通过写入 Redis 来保证,因为 Redis 的 key 和 value 就是天然支持幂等的。当然还有使用 数据库插入法 ,基于数据库的唯一键来保证重复数据不会被插入多条。 不过最主要的还是需要 根据特定场景使用特定的解决方案。