RocketMQ是由阿里自主研发、开源的一款高性能、高吞吐量的分布式消息中间件,使用Java语言进行开发。在2017年9月份由Apach接收,成为 Apache 顶级项目之一,是当前分布式场景下常用的消息队列中间件之一。
接下来本篇文主要是对RocketMQ进行一个简单的介绍,让大家可以快速(十分钟)入门。并把单机版实战例子的过程和其中遇到的坑都在本文写到。
十分钟入门RocketMQ,以及入门消息实战
十分钟入门RocketMQ
RocketMQ 是什么
RocketMQ是一款用Java语言开发的消息中间件,按照典型的消息中间件的消息模式进行设计。RocketMQ具有以下特点:
- 是一个队列模型的消息中间件,具有高性能、高可靠、高实时、分布式特点。
- Producer、Consumer、队列都可以分布式。
- Producer向一些队列轮流发送消息,队列集合称为Topic,Consumer如果做广播消费,则一个consumer实例消费这个Topic对应的所有队列,如果做集群消费,则多个Consumer实例平均消费这个topic对应的队列集合。
- 能够保证严格的消息顺序。
- 提供丰富的消息拉取模式。
- 高效的订阅者水平扩展能力。
- 实时的消息订阅机制。
- 亿级消息堆积能力。
- 较少的依赖。
RocketMQ的构成
RocketMQ 技术架构中有四大角色Broker 、 NameServer 、Producer 、Consumer。
- Broker
主要负责消息的存储、投递和查询以及服务高可用保证。就像是一个消息服务器,生产者生产消息到 Broker ,消费者从 Broker 拉取消息并消费。
- NameServer
NameServer是一个非常简单的Topic路由注册中心,其角色类似Dubbo中的zookeeper,支持Broker的动态注册与发现。主要提供两个功能:Broker管理、路由信息管理 。Broker 会将自己的信息注册到 NameServer 中,并存放在路由表中,消费者和生产者就从 NameServer 中获取路由表然后照着路由表的信息和对应的 Broker 进行通信。
每一个NameServer实例上面都保存一份完整的路由信息。当某个NameServer因某种原因下线了,Broker仍然可以向其它NameServer同步其路由信息。
- Producer
消息发布的角色,支持分布式集群方式部署。即生产者。
- Consumer
消息消费的角色,支持分布式集群方式部署。即消费者。支持以push推,pull拉两种模式对消息进行消费。同时也支持集群方式和广播方式的消费,它提供实时消息订阅机制。
RocketMQ 物理部署结构
如上图所示, RocketMQ的部署结构有以下特点:
- Name Server是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。
- Broker部署相对复杂,Broker分为Master与Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与Name Server集群中的所有节点建立长连接,定时注册Topic信息到所有Name Server。
- Producer与Name Server集群中的其中一个节点(随机选择)建立长连接,定期从Name Server取Topic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。
- Consumer与Name Server集群中的其中一个节点(随机选择)建立长连接,定期从Name Server取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。
RocketMQ 逻辑部署结构
如上图所示,RocketMQ的逻辑部署结构中分为Producer Group、Producer Group、Broke集群。
- Producer Group 生产者组: 代表某一类的生产者,比如我们有多个秒杀系统作为生产者,这多个合在一起就是一个 Producer Group 生产者组,它们一般生产相同的消息。
- Consumer Group 消费者组: 代表某一类的消费者,比如我们有多个短信系统作为消费者,这多个合在一起就是一个 Consumer Group 消费者组,它们一般消费相同的消息。
- Broke集群:主从模式的Broke集群,包含Broke Master、Broke Slave。每个Broke Master中又存在不同的Topic。
Topic 主题: 代表一类消息,比如订单消息,物流消息等等。
一个 Topic 分布在多个 Broker上,一个 Broker 可以配置多个 Topic ,它们是多对多的关系。Topic 中存在多个队列,一个Topic中的多个队列可以分布在多个Broke上,称为分片。
分片是为了解决当某个Topic中 消息量很大时,为了提高并发就采用了多队列。但如果队列全堆积在一个Broke上压力也会太大,所以就产生了分片分布的结构。如果某个 broker 上的队列越多,则意味该 broker 压力越大。
RocketMQ中的消息模型
RockerMQ 中的消息模型就是按照 主题模型 所实现的。或者可以称为 发布订阅模型。
在主题模型中,消息的生产者称为 发布者(Publisher) ,消息的消费者称为 订阅者(Subscriber) ,存放消息的容器称为 主题(Topic) 。
发布订阅模型参照设计模式里面的 观察者模式 进行设计的。
RockerMQ 中的 主题 是使用 队列 去实现的。对于主题模型的实现来说每个消息中间件的底层设计都是不一样的,就比如 Kafka 中的 分区 ,RocketMQ 中的 队列 ,RabbitMQ 中的 Exchange 。
RocketMQ 中的 主题模型 到底是如何实现的呢?可以参考下图:
每个主题中都有多个队列,一个主题中需要维护多个队列是为了提高并发性。如果每个主题中只存在一个队列,也可以实现发布订阅模式,不过这样效率就低了,消费者组也没有用武之地。
在同一个消费者组内一个队列只会被一个消费者消费,每个消费组在每个队列上维护一个消费位置(消费位移offset)。在发布订阅模式中一般会涉及到多个消费者组,而每个消费者组在每个队列中的消费位置都是不同的。如果此时有多个消费者组,那么消息被一个消费者组消费完之后是不会删除的(因为其它消费者组也需要呀),它仅仅是为每个消费者组维护一个 消费位移(offset) ,每次消费者组消费完会返回一个成功的响应,然后队列再把维护的消费位移加一,这样就不会出现刚刚消费过的消息再一次被消费了。
一款消息中间件需要解决哪些问题?
一款消息中间件应该要解决以下这些问题:
发布订阅、消息优先级、消息顺序、消息可靠性、消息过滤(重复消费)、消息持久化。
发布订阅是消息中间件的最基本功能,RockerMQ的主题模式就是发布订阅,上面的内容已经提到过,就不再赘述了。
消息优先级
由于RocketMQ所有消息都是持久化的,所以如果按照优先级来排序,开销会非常大,因此RocketMQ没有特意支持消息优先级,但是可以通过变通的方式实现类似功能,即单独配置一个优先级高的队列,和一个普通优先级的队列, 将不同优先级发送到不同队列即可。
消息顺序
RocketMQ 在主题上是无序的、它只有在队列层面才是保证有序的。RocketMQ可以严格的保证消息有序。
说到消费顺序必要要先提一下:普通顺序 和 严格顺序 。
- 所谓普通顺序是指消费者通过消费同一个队列收到的消息是有顺序的 ,消费不同消息队列收到的消息则可能是无顺序的。普通顺序消息在 Broker 重启情况下不会保证消息顺序性 (短暂时间) 。
- 所谓严格顺序是指消费者收到的 所有消息 均是有顺序的。严格顺序消息即使在异常情况下也会保证消息的顺序性 。
但是,严格顺序看起来虽好,实现它可会付出巨大的代价。如果你使用严格顺序模式,Broker 集群中只要有一台机器不可用,则整个集群都不可用。现在主要场景也就在 binlog 同步。
要保持普通顺序,那么在生产消息的时候生产者可以将同一语义都放到同一个队列中,例如:一个订单产生了3条消息,分别是订单创建,订单付款,订单完成。消费时,要按照这个顺序消费才能有意义。可以使用 Hash取模法来保证同一个订单在同一个队列中就行了。
消息可靠性
影响消息可靠性的几种情况:
- Broker正常关闭
- Broker异常Crash
- OS Crash
- 机器掉电,但是能立即恢复供电情况。
- 机器无法开机(可能是cpu、主板、内存等关键设备损坏)
- 磁盘设备损坏。
(1)、(2)、(3)、(4)四种情况都属于硬件资源可立即恢复情况,RocketMQ在这四种情况下能保证消息不丢,或者丢失少量数据(依赖刷盘方式是同步还是异步)。
(5)、(6)属于单点故障,且无法恢复,一旦发生,在此单点上的消息全部丢失。RocketMQ在这两种情况下,通过异步复制,可保证99%的消息不丢,但是仍然会有极少量的消息可能丢失。通过同步双写技术可以完全避免单点,同步双写势必会影响性能,适合对消息可靠性要求极高的场合,例如与Money相关的应用。
RocketMQ从3.0版本开始支持同步双写。
保证可靠性的投递方式
为了保证消息能可靠的投递和消费,RocketMQ提供了以下三种投递方式:Low Latency Messaging、At least Once、Exactly Only Once。
- Low Latency Messaging
在消息不堆积情况下,消息到达Broker后,能立刻到达Consumer。RocketMQ使用长轮询Pull方式,可保证消息非常实时,消息实时性不低于Push。
- At least Once
是指每个消息必须投递一次。
RocketMQ Consumer先pull消息到本地,消费完成后,才向服务器返回ack,如果没有消费一定不会ack消息,所以RocketMQ可以很好的支持此特性。
- Exactly Only Once
发送消息阶段,不允许发送重复的消息。
消费消息阶段,不允许消费重复的消息。
只有以上两个条件都满足情况下