楔子:项目中,开发很容易忽视的一点是,只想到或者只想做自己应该做的事。这是非常有碍自己进步和自我放弃的一个心态或决策。实际上,只要你置身于万物中,每个层阶都是这样。所以,相比拎包入住的租赁市场的成熟与便利,开包即用的项目开发,是团队协作的精华,而我们要做的就是制作那些精华。
1. 枯燥无味
不管是单击游戏,还是吃鸡游戏,抛开主观情感,其路子本质基本上都是打怪升级成就无量至尊。所以,无论是主线还是支线任务,是副本还是打野,攒经验升级前,我们先要做的基本上都是 是什么、为什么、做什么、怎么做(用)、怎么做(用)能快速高效等等,只是游戏让这些问题都变得潜移默化顺乎所以。
那 MQ 是什么?我为什么要 Get 她?她能做什么?我要怎么做/用?什么时候用?怎么做/用可以更快速高效?
理论的产生从是来枯燥无味,不妨让理解理论的过程变得有趣一点。
2. 大水池子
我们可以想像有一个大水池子,里面的水时而被使用殆尽,时而又被注入新水,常此以往,这池水物泽苍生大地经久不息。而注入新水为生产者(Producer)、使用水源为消费者(Consumer),这个大水池子就是服务器(Name Server、Broker),大水池2、大水池3 ... 的互通就是服务器集群,消费工具就是水管管道(订阅 Topic)。
- 居民区B对水池2的消费使用突增,水池1、水池3的水源会互通调节注入,以防水池2被使用干涸,这可以理解为服务器的削峰填谷。
- 居民区B的B001户人家使用大水池的水,并不会影响居民区A的A001户人家;且B001户人家要开始洗衣做饭拖地啦。这个指令一下,洗衣机要用水开始洗衣,厨房要放水洗菜洗碗,阳台要放水洗拖把,但是并不会相互产生制约,因为没有只能洗衣放水而做饭停水,也没有只能洗菜有水而洗拖把没水的顾虑。这就是异步解耦。
- 顺序消息也好理解,比如B001户的小朋友哭着要吃牛奶了,这个指令的前置条件是奶粉+温开水,开水不行,小朋友马上就要喝的,冷水也不行,所以这个收到泡牛奶的消息后,指令就可以拆解为 凉白开 + 开水 + 奶粉 + 轻摇溶解 + 试温 一系列操作后才能给小朋友喝。不然,小朋友不会喝,还哭得更厉害。抑或者洗澡,我们肯定是要在抹完沐浴露后再用水冲洗身子,再用干浴巾擦,最后穿上衣服,而不是抹完沐浴露后不冲也不擦直接穿衣服。这就是完成一条指令信息遵循的先后动作,颠倒不得。
- 分布式事务消息可以想像成B001户做中饭,这个信息指令可以分工拆解快速的完成:小朋友他爸放水淘米煮饭,小朋友他妈放水洗菜,小朋友奶奶炒菜,小朋友爷爷带娃,这样的流程下来,一家人就可以很快吃到一桌好饭好菜了。相比奶奶一个人带娃又淘米洗菜炒菜要方便快捷得不要太多。
这就是 RocketMQ 的特性和应用场景。
3. 生而不凡
3.1. 那 MQ 是什么?
记忆中第一次玩游戏时,对 RPG 这个名词陌生又熟悉,但我知道自己在玩一款 角色扮演游戏(Role-playing game,简称 RPG)。了解一个事物一般是从 TA 的名字或名称开始的,也有从印象从感觉开始的,但是也有很多例外,像花儿,像TA,像MQ。
消息队列(Message Queue,简称 MQ),是构建分布式互联网应用的基础设施,通过 MQ 实现的松耦合架构设计可以提高系统可用性以及可扩展性,既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。是适用于现代应用的最佳设计方案。具体常见的有如订单消息,支付消息等等。
3.2. 什么是 RocketMQ 呢?
就像你在了解 TRPG (桌上角色扮演游戏,Table-top Role-Playing Game);SRPG (策略角色扮演游戏,Strategy Role-Playing Game) 一样,RocketMQ 也是 MQ 消息队列家庭中的一个系,因为除了 RocketMQ,还有 RabbitMQ、Kafka、MQTT 等等。
Apache RocketMQ 是由阿里巴巴自研,并捐赠给 Apache 基金会,并于开源社区共建的消息中间件,2017年9月25日成为 Apache 的顶级项目。该产品服务于阿里巴巴集团已超过 13 年,经过交易核心链路反复打磨与历年双十一高并发场景的严苛考验,已然是一个真正具备低延迟、高并发、高可用、高可靠,可支撑万亿级数据洪峰的分布式消息中间件。
RocketMQ 经历了三个主要版本迭代:
- Metaq(Metamorphosis) 1.x
由开源社区 killme2008(庄晓丹)维护,最后一次 MetaQ 的更新时间为2013年。 - Metaq 2.x
于2012 年10 月份上线,在淘宝内部被广泛使用。 - RocketMQ 3.x
基于阿里巴巴公司内部开源共建原则,RocketMQ 项目只维护核心功能,且去除了所有其他运行时依赖,核心功能最简化。每个产品的个性化需求都在 RocketMQ 项目之上进行深度定制。
官网:http://rocketmq.apache.org
下载:http://rocketmq.apache.org/dowloading/releases/
GitHub:https://github.com/apache/rocketmq
3.3. 你的名字
做任务,不管是主线还是支线,你都需要在游戏里面找到具体某个“人”(NPC);AT 代表攻击力(Attack);DF 代表防御力(Defense)等等,玩游戏也要玩得得心应手玩得专业的话,就不免需要了解很多的游戏专业术语。
了解 MQ 也是一样,需要了解一些核心概念:
- 消息生产者(Producer):
负责生产消息,一般由业务系统负责生产消息。一个消息生产者会把业务应用系统里产生的消息发送到 Broker 服务器。RocketMQ 提供多种发送方式:**同步发送、异步发送、顺序发送、单向发送。**同步和异步方式均需要 Broker 返回确认信息,单向发送不需要。详见第三节。 - 消息消费者(Consumer):
负责消费消息,一般是后台系统负责异步消费。一个消息消费者会从 Broker 服务器拉取消息、并将其提供给应用程序。从用户应用的角度而言提供了两种消费形式:拉取式消费、推动式消费。详见第三节。 - 主题(Topic):
表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是 RocketMQ 进行消息订阅的基本单位。 - 标签(Tag):
为消息设置的标志,用于同一主题下区分不同类型的消息。来自同一业务单元的消息,可以根据不同业务目的在同一主题下设置不同标签。标签能够有效地保持代码的清晰度和连贯性,并优化 RocketMQ 提供的查询系统。消费者可以根据 Tag 实现对不同子主题的不同消费逻辑,实现更好的扩展性。 - 消息(Message):
消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于一个主题。RocketMQ 中每个消息拥有唯一的 Message ID,且可以携带具有业务标识的 Key。系统提供了通过 Message ID 和 Key 查询消息的功能。 - 代理服务器(Broker Server):
消息中转角色,负责存储消息、转发消息。代理服务器在 RocketMQ 系统中负责接收从生产者发送来的消息并存储、同时为消费者的拉取请求作准备。代理服务器也存储消息相关的元数据,包括消费者组、消费进度偏移和主题和队列消息等。 - 名字服务(Name Server):
名称服务充当路由消息的提供者。生产者或消费者能够通过名字服务查找各主题相应的 Broker IP 列表。多个 Namesrv 实例组成集群,但相互独立,没有信息交换。 - 生产者组(Producer Group):
同一类 Producer 的集合,这类 Producer 发送同一类消息且发送逻辑一致。如果发送的是事物消息且原始生产者在发送之后崩溃,则 Broker 服务器会联系同一生产者组的其他生产者实例以提交或回溯消费。 - 消费者组(Consumer Group):
同一类 Consumer 的集合,这类 Consumer 通常消费同一类消息且消费逻辑一致。消费者组使得在消息消费方面,实现负载均衡和容错的目标变得非常容易。要注意的是,消费者组的消费者实例必须订阅完全相同的 Topic。RocketMQ 支持**两种消息模式:集群消费(Clustering)和广播消费(Broadcasting)。**详见第五节。
3.4. 那我为什么要 Get 到她呢?
就像在吃鸡游戏里面你搞到一把98K狙击步枪的狂肆;就像在诛仙游戏里你合成了一套+12装备的狂喜;也像 MQ,她们功能特点和功能特点产生的作用或效果都会让你狂浪。
RocketMQ 的主要特点有:
- 灵活可扩展性
RocketMQ 天然支持集群,其核心四组件(Name Server、Broker、Producer、Consumer)每一个都可以在没有单点故障的情况下进行水平扩展。 - 支持顺序消息
可以保证消息消费者按照消息发送的顺序对消息进行消费。顺序消息分为全局有序和局部有序,一般推荐使用局部有序,即生产者通过将某一类消息按顺序发送至同一个队列来实现。 - 支持事务消息
RocketMQ 除了支持普通消息,顺序消息之外还支持事务消息,支持事务消息绝对是亮点,这个特性对于分布式事务来说提供了一种最佳解决方案。 - 消息回溯消费
回溯消费是指消费者已经消费成功的消息,由于业务上需求需要重新消费,RocketMQ 支持按照时间回溯消费,时间维度精确到毫秒,可以向前回溯,也可以向后回溯。 - 多种消息过滤方式
5.1 消息过滤分为在服务端过滤和在消费端过滤
5.2 服务端过滤时可以按照消息消费者的要求做过滤,优点是减少不必要消息传输,缺点是增加了消息服务器的负担,实现相对复杂
5.3 消费端过滤则完全由具体应用自定义实现,这种方式更加灵活,缺点是很多无用的消息会传输给消息消费者 - 亿级消息堆积能力
RocketMQ 采用零拷贝原理实现超大的消息的堆积能力,在堆积海量消息后依然保持写入低延迟。
3.5. 她能做什么?
RocketMQ 的应用场景非常多,主要是可以实现分布式系统业务解耦:
- 削峰填谷:诸如秒杀、抢红包、企业开门红等大型活动时皆会带来较高的流量脉冲,或因没做相应的保护而导致系统超负荷甚至崩溃,或因限制太过导致请求大量失败而影响用户体验,消息队列 MQ 可提供削峰填谷的服务来解决该问题。
- 异步解耦:交易系统作为淘宝/天猫主站最核心的系统,每笔交易订单数据的产生会引起几百个下游业务系统的关注,包括物流、购物车、积分、流计算分析等等,整体业务系统庞大而且复杂,消息队列 MQ 可实现异步通信和应用解耦,确保主站业务的连续性。
- 顺序收发:细数日常中需要保证顺序的应用场景非常多,比如证券交易过程时间优先原则,交易系统中的订单创建、支付、退款等流程,航班中的旅客登机消息处理等等。与先进先出(First In First Out,缩写 FIFO)原理类似,消息队列 MQ 提供的顺序消息即保证消息 FIFO。
- 分布式事务一致性:交易系统、支付红包等场景需要确保数据的最终一致性,大量引入消息队列 MQ 的分布式事务,既可以实现系统之间的解耦,又可以保证最终的数据一致性。
- 大数据分析:数据在“流动”中产生价值,传统数据分析大多是基于批量计算模型,而无法做到实时的数据分析,利用阿里云消息队列 MQ 与流式计算引擎相结合,可以很方便的实现将业务数据进行实时分析。
- 分布式缓存同步:天猫双 11 大促,各个分会场琳琅满目的商品需要实时感知价格变化,大量并发访问数据库导致会场页面响应时间长,集中式缓存因为带宽瓶颈限制商品变更的访问流量,通过消息队列 MQ 构建分布式缓存,实时通知商品数据的变化。
4. 江湖奇才
RocketMQ 系统架构部署图:
4.1. 系统架构
1)Producer:消息发布的角色,支持分布式集群方式部署。Producer 通过 MQ 的负载均衡模块选择相应的 Broker 集群队列进行消息投递,投递的过程支持快速失败并且低延迟。
2)Consumer:消息消费的角色,支持分布式集群方式部署。支持以 push 推,pull 拉两种模式对消息进行消费。同时也支持集群方式和广播方式的消费,它提供实时消息订阅机制,可以满足大多数用户的需求。
3)NameServer:NameServer 是一个非常简单的 Topic 路由注册中心,其角色类似 Dubbo 中的 zookeeper,支持 Broker 的动态注册与发现。主要包括两个功能:
- Broker 管理,NameServer 接受 Broker 集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查 Broker 是否还存活;
- 路由信息管理,每个 NameServer 将保存关于 Broker 集群的整个路由信息和用于客户端查询的队列信息。然后 Producer 和 Conumser 通过 NameServer 就可以知道整个 Broker 集群的路由信息,从而进行消息的投递和消费。NameServer 通常也是集群的方式部署,各实例间相互不进行信息通讯。Broker 是向每一台 NameServer 注册自己的路由信息,所以每一个 NameServer 实例上面都保存一份完整的路由信息。当某个 NameServer 因某种原因下线了,Broker 仍然可以向其它 NameServer 同步其路由信息,Producer、Consumer 仍然可以动态感知 Broker 的路由的信息。
4)BrokerServer:Broker 主要负责消息的存储、投递和查询以及服务高可用保证,为了实现这些功能,Broker 包含了以下几个重要子模块:
- Remoting Module:整个 Broker 的实体,负责处理来自 clients 端的请求。
- Client Manager:负责管理客户端(Producer/Consumer)和维护 Consumer 的 Topic 订阅信息
- Store Service:提供方便简单的API接口处理消息存储到物理硬盘和查询功能。
- HA Service:高可用服务,提供 Master Broker 和 Slave Broker 之间的数据同步功能。
- Index Service:根据特定的 Message key 对投递到 Broker 的消息进行索引服务,以提供消息的快速查询。
4.2. 网络部署
【RocketMQ部署流程一览】
4.2.1 部署架构图解
- Name Server:是一个几乎无状态节点,可集群部署,在消息队列 MQ 中提供命名服务,更新和发现 Broker 服务。
- Broker:消息中转角色,负责存储消息,转发消息。分为 Master Broker 和 Slave Broker,一个 Master Broker 可以对应多个 Slave Broker,但是一个 Slave Broker 只能对应一个 Master Broker。Broker 启动后需要完成一次将自己注册至 Name Server 的操作;随后每隔 30s 定期向 Name Server 上报 Topic 路由信息。
- Producer:与 Name Server 集群中的其中一个节点(随机)建立长链接(Keep-alive),定期从 Name Server 读取 Topic 路由信息,并向提供 Topic 服务的 Master Broker 建立长链接,且定时向 Master Broker 发送心跳。
- Consumer:与 Name Server 集群中的其中一个节点(随机)建立长连接,定期从 Name Server 拉取 Topic 路由信息,并向提供 Topic 服务的 Master Broker、Slave Broker 建立长连接,且定时向 Master Broker、Slave Broker 发送心跳。Consumer 既可以从 Master Broker 订阅消息,也可以从 Slave Broker 订阅消息,订阅规则由 Broker 配置决定。
4.2.2 集群工作流程
- 启动 NameServer,NameServer 起来后监听端口,等待 Broker、Producer、Consumer 连上来,相当于一个路由控制中心。
- Broker 启动,跟所有的 NameServer 保持长连接,定时发送心跳包。心跳包中包含当前 Broker 信息(IP+端口等)以及存储所有 Topic 信息。注册成功后,NameServer 集群中就有 Topic 跟 Broker 的映射关系。
- 收发消息前,先创建 Topic,创建 Topic 时需要指定该 Topic 要存储在哪些 Broker 上,也可以在发送消息时自动创建 Topic。
- Producer 发送消息,启动时先跟 NameServer 集群中的其中一台建立长连接,并从 NameServer 中获取当前发送的 Topic 存在哪些 Broker 上,轮询从队列列表中选择一个队列,然后与队列所在的 Broker 建立长连接从而向 Broker 发消息。
- Consumer 跟 Producer 类似,跟其中一台 NameServer 建立长连接,获取当前订阅 Topic 存在哪些 Broker 上,然后直接跟 Broker 建立连接通道,开始消费消息。
**注意:**图文中所提及的消息队列 MQ 的服务端或者服务器包含 Name Server、Broker 等。服务端不等同于 Broker。
参考资料:
RocketMQ 官网:http://rocketmq.apache.org/docs/motivation/
阿里云消息队列 MQ:https://help.aliyun.com/document_detail/29532.html
阿里巴巴中间件团队:http://jm.taobao.org/2017/03/03/RocketMQ-future-idea/