1 RocketMq简介

2016-01-26 22:56:21 3,528

前言

本文档旨在描述 RocketMQ 的多个关键特性的实现原理,并对消息中间件遇到的各种问题迕行总结,阐述RocketMQ 如何解决这些问题。 文中主要引用了 JMS 规范与 CORBA Notification 规范,规范为我们设计系统指明了方向,但是仍有不少问题规范没有提及,对于消息中间件又至关重要。RocketMQ 并不遵循任何规范,但是参考了各种规范不同类产品的设计思想。


产品发展历史

大约经历了三个主要版本迭代

一、 Metaq(Metamorphosis) 1.x

由开源社区 killme2008 维护,开源社区非常活跃。

https://github.com/killme2008/Metamorphosis

二、 Metaq 2.x

亍 2012 年 10 月份上线,在淘宝内部被广泛使用。

三、 RocketMQ 3.x

基于公司内部开源共建原则, RocketMQ 项目只维护核心功能,且去除了所有其他运行时依赖,核心功能最简化。每个 BU 的个性化需求都在 RocketMQ 项目之上进行深度定制。 RocketMQ 向其他 BU 提供的仅仅是Jar 包,例如要定制一个 Broker,那么只需要依赖 rocketmq-broker 这个 jar 包即可,可通过 API 迕行交互,如果定制 client,则依赖 rocketmq-client 这个 jar 包,对其提供的 api 进行再封装。

开源社区地址:

https://github.com/alibaba/RocketMQ


专业术语

Producer

消息生产者,负责产生消息,一般由业务系统负责产生消息。


Consumer

消息消费者,负责消费消息,一般是后台系统负责异步消费。


Push Consumer

Consumer 的一种,应用通常向 Consumer 对象注册一个 Listener 接口,一旦收到消息,Consumer 对象立刻回调

Listener 接口方法。


Pull Consumer

Consumer 的一种,应用通常主动调用 Consumer 的拉消息方法从 Broker 拉消息,主动权由应用控制。


Producer Group

一类 Producer 的集合名称,这类 Producer 通常发送一类消息,且发送逻辑一致。 通常具有同样属性(处理的消息种类-topic、以及消息处理逻辑流程—分布式多个客户端)的一些producer可以归为同一个group。在事务消息 机制中,如果某条发送某条消息的producer-A宕机,使得事务消息一直处于PREPARED状态并超时,则broker会回查同一个group的其 他producer,确认这条消息应该commit还是rollback。


Consumer Group

一类 Consumer 的集合名称,这类 Consumer 通常消费一类消息,且消费逻辑一致。同一个group中的消费者,可以共同消费topic中的信息,达到分布式并行处理的功能。


Topic

消息的逻辑管理单位


Message Queue

消息的物理管理单位。一个Topic下可以有多个Queue,Queue的引入使得消息的存储可以分布式集群化,具有了水平扩展能力。

在 RocketMQ 中,所有消息队列都是持久化,长度无限的数据结构,所谓长度无限是挃队列中的每个存储单元都是定长,访问其中的存储单元使用 Offset 来访问,offset 为 java long 类型,64 位,理论上在 100年内不会溢出,所以认为是长度无限,另外队列中只保存最近几天的数据,之前的数据会按照过期时间来删除。

也可以认为 Message Queue 是一个长度无限的数组,offset 就是下标。


Broker

消息中转角色,负责存储消息,转发消息,一般也称为 Server。在 JMS 规范中称为 Provider。


广播消费

一条消息被多个 Consumer 消费,即使这些 Consumer 属于同一个 Consumer Group,消息也会被 Consumer Group 中的每个 Consumer 都消费一次,广播消费中的 Consumer Group 概念可以认为在消息划分方面无意义。

在 CORBA Notification 规范中,消费方式都属于广播消费。

在 JMS 规范中,相当于 JMS publish/subscribe model


集群消费

一个 Consumer Group 中的 Consumer 实例平均分摊消费消息。例如某个 Topic 有 9 条消息,其中一个Consumer Group 有 3 个实例(可能是 3 个进程,或者 3 台机器),那么每个实例只消费其中的 3 条消息。

在 CORBA Notification 规范中,无此消费方式。

在 JMS 规范中,JMS point-to-point model 与之类似,但是 RocketMQ 的集群消费功能大等于 PTP 模型。

因为 RocketMQ 单个 Consumer Group 内的消费者类似于 PTP,但是一个 Topic/Queue 可以被多个 Consumer Group 消费。


消费进度管理

http://www.open-open.com/doc/view/1b777e245dbd49df85b45ecc36c84489

rocketmq的broker端,不负责消息推送,无论消费者是否消费,都将消息存储起来。谁要获取消息,就向broker发请求获取消息。消费记录由consumer来维护。RocketMq提供两种方式保留消费记录:一种是保存在consumer所在的服务器上;另一种是保存在broker服务器上。用户还可以自己实现相应的消费进度存储接口。

默认情况下,采用集群消费(CLUSTERING),会将记录保存在broker端。而采用广播消费(BROADCASTING)则会将记录保存在本地。


顺序消息

消费消息的顺序要同发送消息的顺序一致,在 RocketMQ 中,主要指的是局部顺序,即一类消息为满足顺序性,必须 Producer 单线程顺序发送,且发送到同一个队列,这样 Consumer 就可以按照 Producer 发送的顺序去消费消息。

用户实现MessageQueueSelector为某一批消息(通常是有相同的唯一标示id),选择同一个Queue,则这一批消息的消费将是顺序消息(并由同一个consumer完成消息)


普通顺序消息

顺序消息的一种,正常情况下可以保证完全的顺序消息,但是一旦发生通信异常,Broker 重启,由于队列总数发生发化,哈希取模后定位的队列会发化,产生短暂的消息顺序不一致。

如果业务能容忍在集群异常情况(如某个 Broker 宕机或者重启)下,消息短暂的乱序,使用普通顺序方式比较合适。


严格顺序消息

顺序消息的一种,无论正常异常情况都能保证顺序,但是牺牲了分布式 Failover 特性,即 Broker 集群中只

要有一台机器不可用,则整个集群都不可用,服务可用性大大降低。

如果服务器部署为同步双写模式,此缺陷可通过备机自动切换为主避免,不过仍然会存在几分钟的服务不可用。(依赖同步双写,主备自动切换,自动切换功能目前并未实现)

目前已知的应用只有数据库 binlog 同步强依赖严格顺序消息,其他应用绝大部分都可以容忍短暂乱序,推荐使用普通的顺序消息。


事务消息

这样的消息有多个状态,并且其发送是两阶段的。第一个阶段发送是PREPARE状态的消息,此时Consumer是看不到这种状态的消息的,发送完毕后回调用户的TransactionalExecutor接口,执行相应的事务操作(如数据库),当事务操作成功时,则对此条消息返回commit。让broker对该消息执行commit操作,成为commit状态的消息对consumer是可见的。


总览

rocketmq以Topic来管理不同应用的消息。对于生产者而言,发送消息时,需要指定消息的Topic,对于消费者而言,在启动后,需要订阅相应的Topic,然后可以消费响应的消息。在物理实现上,一个Topic由多个Queue组成,采用多个Queue的好处是,可以将Broker存储分布式化,提供系统性能。

RocketMQ中,producer将消息发送给broker时,需要指定发送到哪一个队列中,默认情况下,producer会轮询的将消息发送到每个队列中(所有Broker下的Queue合并成一个list去轮询)。


对于Consumer而言,会为每个Consumer分配固定的队列(如果队列总数没有发生变化),我们将这个Consumer称之为这个Queue的Owner,consumer从固定的队列中去拉取没有消费的信息进行处理。下面的图实际上显示了上面的Consumer是2个queue的Owner,下面的Consumer是一个Queue的Owner。当producer往Topic中发送消息时,会随机的往其中一个queue中发送消息。那么如何保证一个consumer group中,只有一个consumer消费呢?因为发送到一个队列,这个队列必定属于其中某一个owner,因此只能由这个消费者处理。当Queue数小于一个consumer group中的consumer数量时,必定有的consumer会空跑,因为即使一个consumer只own一个queue,还是多出的consumer。因此我们一般情况下,会将队列数设置的大于consumer group中consumer的数量。因为基本上一个consumer group对应一个应用,一个consumer对应一个部署实例,即使我们部署了10个实例,假设queue只有2个,必定有8个部署实例在空跑。所以在默认情况下,我们要将Topic的queue设置的大一点。


逻辑消费图:

QQ截图20160126225439.png

物理消费图:

QQ截图20160126225537.png