vlambda博客
学习文章列表

分布式消息队列(上):主流MQ的二三事



预计阅读时间:15分钟


在电商系统、即时通讯系统、日志处理等等场景中都有消息队列中间件的身影,近些年来,分布式消息队列技术也得到了越来越多的关注。现在市面上的消息队 列中间件种类众多,并且各有所长,如何理解不同消息队列中间件的优缺点,以及各自的适用场景就非常重要。
例如,用户量很少的内部系统,直接采用Kafka,并且美其名曰是为了高性能和可扩展性,那就是大材小用,有点过度设计的嫌疑。目前常用的分布式消息队列中间件有:Kafka、ActiveMQ、RabbitMQ 及 RocketMQ。接下来这篇文章,将对主流的消息队列中间件进行比较和解析。

01
—
什么时候需要消息队列
1. 异步处理
处理如短信下发、状态推送、用户注册、数据同步等功能,提高系统的并发能力,集中力量处理重要的部分(同步处理),将非核心功能丢给MQ。

2. 系统解耦
可在模块、服务、接口等不同粒度上实现解耦。

3. 重试补偿
在跨机器数据传输的整个过程中,只要任意一个环节出错,都会导致问题,可以通过MQ的重试补偿机制去尽可能的处理掉这些异常。

4. 流量削锋
对于秒杀场景下的下单处理,服务器收到消息后,首先写入消息队列,然后按照自己的消息处理能力做处理。

5. 日志处理
可以定时将日志写入MQ,并且主动订阅日志记录。

02
—
消息队列的介绍
消息队列这种设计的核心就是基于消息的通信模式,从关注处理到关注通知,它具有低耦合,可靠投递,广播,流量控制以及最终一致性等功能。与RPC所对应,是实现“异步RPC”的主要手段。接下来会对主流消息队列的基本信息进行介绍,并对四者进行比较分析。
1. 4种主流消息队列的介绍
(1)Kafka
Kafka起初是由LinkedIn公司采用Scala语言开发的一个分布式、多分区、多副本且基于zookee per协调的分布式消息系统。它是一种高吞吐量的分布式发布订阅消息系统,以 可水平扩展 和 高吞吐率 而被广泛使用。目前越来越多的开源分布式处理系统如Cloudera、Apache Storm、Spark、Flink等都支持与Kafka集成。

(2)ActiveMQ
ActiveMQ是Apache下的一个子项目, 是一个完全支持 JMS1.1 和 J2EE 1.4 规范的 JMS Provider 实现,非常快速,支持多种语言的客户端和协议,少量代码就可以高效地实现高级应用场景。

(3)RabbitMQ
RabbitMQ是使用Erlang编写的一个开源的消息队列,支持多种客户端,如 Pyt hon、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP 等,支持 AJAX、持久化。RabbitMQ相对重量级,更适合于企业级的开发。

(4)RocketMQ
RocketMQ是阿里系下开源的一款纯Java、分布式、队列模型的消息中间件,原名 Metaq,是阿里参照 kafka设计思想使用java实现的一套MQ。RocketMQ 是一个具有高性能、高可靠、低延迟、分布式的万亿级容量,且可伸缩的分布式消息和流平台。它由 Name Servers、Brokers、 Producers 和 Consumers 四个部分组成。

2.  4种主 流消 息队列的对比

特性
Kafka
ActiveMQ
RabbitMQ
RocketMQ
开发语言
Scala
Java
Erlang
Java

支持的协议

自定义的一套(基于TCP)
OpenWire、STOMP、REST、XMPP、AMQP
AMQP
自定义的一套
存储
内存&磁盘&数据库,支持大量堆积
内存&磁盘&数据库,支持少量堆积
内存&磁盘,支持少量堆积
磁盘,支持大量堆积

消息事务

支持
支持
支持
支持

管理界面

一般
一般
好
有管理后台
可用性
非常高(分布式)
高(主从)
高(主从)
非常高(分布式)

吞吐量TPS

极大
比较大
比较大
大

顺序消息

支持
不 支持
不 支持
支持
消息回溯
支持指定分区offset位置的回溯
不支持
不支持
支持指定时间点的回溯


03
—
主流消息队列是如何存储的
篇幅有限,下面仅以Kafka和RocketMQ为例讲解一下是如何存储的,以及写入和读取的过程。这两者的存储结构有很多共通的地方,都是采用消息文件+索引文件的存储方式。
1、Kafka
Kafka 的存储以 Partition 为单位,每个 Partition 包含一组消息文件(Segment file)和一组索引文件(Index),并且消息文件和索引文件一一对应,具有相同的文件名(但文件扩展名不一样),文件名就是这个文件中第一条消息的索引序号。
每个索引中保存索引序号(也就是这条消息是这个分区中的第几条消息)和对应的消息在消息文件中的绝对位置。在索引的设计上,Kafka 采用的是稀疏索引,为了节省存储空间,它不会为每一条消息都创建索引,而是每隔几条消息创建一条索引。
(1)写入消息
在消息文件尾部连续写入,一个文件写满了再写下一个文件。
(2)读取消息
  • 首先根据文件名找到所在的索引文件

  • 然后用二分法遍历索引文件内的索引,找到离目标消息最近的索引

  • 再去消息文件中找到这条最近的索引指向的消息位置,从这个位置开始顺序遍历消息文件,直到找到目标消息


2、RocketMQ

RocketMQ的存储以 Broker 为单位,它的存储也是分为消息文件和索引文件,但是在 RocketMQ 中,每个 Broker 只有一组消息文件,它把在这个 Broker 上的所有主题的消息都存在这一组消息文件中。

索引文件和 Kafka 一样,是按照主题和队列分别建立的,每个队列对应一组索引文件,这组索引文件在 RocketMQ 中称为 ConsumerQueue。RocketMQ 中的索引是定长稠密索引,它为每一条消息都建立索引,每个索引的长度(注意不是消息长度)是固定的 20 个字节。

(1)写入消息

写入消息的时候,Broker 上所有主题、所有队列的消息按照自然顺序追加写入到同一个消息文件中,一个文件写满了再写下一个文件。

(2)读取消息

  • 首先根据队列的消息序号,计算出索引的全局位置
(索引序号 x 索引固定长度 20)
  • 然后直接读取这条索引
  • 再根据索引中记录的消息的全局位置,找到消息



【微语】

我们似乎总会在某一年,爆发性地长大,爆发性地觉悟,爆发性地知道某个真相,让原本没有什么意义的时间的刻度,成了一道分界线。

——韩松落《老灵魂》

分布式消息队列(上):主流MQ的二三事
文章推荐