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的二三事
文章推荐