0%

消息队列选型

对比常见消息队列特点及使用场景

消息队列介绍

消息队列作用

  1. 异步处理
  2. 削峰限流
  3. 解耦
  4. 实现分布式事务
  5. 保证消息顺序处理
  6. 延时处理
  7. 数据流处理

异步处理

将用户请求中比较耗时的操作发送到消息队列进行异步处理,减少用户等待。

应用:疫情打卡 Excel 文件导出时,由于文件较大耗时可能较长,可以将导出任务交给消息队列处理。

削峰限流

将短时间内的高并发任务存储在消息队列中,后端服务慢慢根据自己的能力去消费这些消息,避免直接把后端服务打垮。(其实就是漏桶算法)

解耦

基于发布订阅模式,不存在直接调用关系,任务的产生方不需知道由谁来提供服务,任务的服务者也不需要直到是谁发起的任务,可以降低系统的耦合度。

分布式事务

可以将任务从生产到消费的整个过程定义为一个原子操作,要么都成功,要么都失败。

顺序保证

可以使用消息队列保证消息处理的顺序性,防止出现用户先插入后删除,执行时确实先删除后插入的情况出现。

延时处理

消息发送后不会被立即消费,而是达到指定时间之后再消费,比如 10 分钟后取消未支付的订单。

数据流处理

针对分布式系统产生的海量数据流,如业务日志、监控数据、用户行为等,消息队列可以实时或批量收集这些数据,并将其导入到大数据处理引擎中,实现高效的数据流管理和处理。

狭义消息队列

RabbitMQ

RabbitMQ 是使用 Erlang 实现的消息队列,基于 AMQP 协议,也提供了其他的协议支持。其

RocketMQ

RocketMQ 是阿里开源的一个高性能、高可靠、高实时的分布式消息队列,设计是参考了 Kafka 并对消息的可靠传输及事务性做了优化,目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景。

Kafka

Kafka 是一个开源流处理平台,最初设计用于收集传输才处理数据流,后来开始增加了不少功能,比如防止消息丢失,解决数据可靠性问题等,现在已经很完善也被用作消息队列,其采用批量处理等思想,性能很强,可以做到极致的吞吐率。

Pulsar

Apache Pulsar 是 Apache 软件基金会的顶级项目,是下一代云原生分布式消息流平台,集消息、存储、轻量化函数式计算为一体,采用计算与存储分离架构设计,支持多租户、持久化存储、多机房跨区域数据复制,具有强一致性、高吞吐、低延时及高可扩展性等流数据存储特性。

对比

这几种消息队列经常被放在一起来比较的,这里也总结以下这几个消息队列的特点:

特性 RabbitMQ RocketMQ Kafka Pulsar
协议支持 支持 AMQP、STOMP、MQTT 等 自定义协议,支持 JMS 和 MQTT 仅支持 Kafka 协议 自定义协议,支持 MQTT、JMS、AMQP 等
消息路由 灵活复杂 简单 基于分区 支持多租户、分区、强大的消息路由功能
性能 中等 较高 极高 高性能,支持大规模集群扩展
延迟消息 插件支持 原生支持 无直接支持,需手动实现 原生支持延迟队列和定时消息
持久化与存储 支持持久化,但性能较弱 支持高可靠性存储 强大的日志存储机制 支持多层次存储,消息持久化,PB级存储
消息顺序 支持严格顺序(单一队列) 支持顺序消息(通过分区) 支持顺序消息(通过分区) 支持严格顺序消息和无序消息
水平扩展性 扩展性有限 支持水平扩展 高度可扩展,支持大规模数据流 非常高,支持独立存储与计算层扩展,PB级存储
多租户支持 不支持 不支持 不支持 支持多租户、命名空间和资源配额管理
流式处理 不适用 不适用 与 Kafka Streams 集成支持流式处理 原生支持流式处理和集成 Pulsar Functions
使用场景 适合小型到中型的消息传递,复杂路由需求 适合分布式事务、金融、电商等 适合日志收集、大数据流处理 适合多租户、PB级消息量、高吞吐量的应用

广义消息队列

MQTT

MQTT(Message Queuing Telemetry Transport) 是一种轻量级、基于发布-订阅模式的消息传输协议,适用于资源受限的设备和低带宽、高延迟或不稳定的网络环境。它在物联网应用中广受欢迎,能够实现传感器、执行器和其它设备之间的高效通信。而且也可以应用在网络直播互动、手机消息推送等行业场景。详细可参考 [[MQTT协议]]

其实 MQTT 并不是算是一个消息队列,只是两者的特性比较相近,但他们的出发点和应用场景是有着显著不同的:

  • 消息队列作为一种中间件,主要用于服务端应用之间的消息存储与转发,数据量大但客户端数量少。
  • MQTT 是一种消息传输协议,主要用于物联网设备间的消息传递,特点是连接的设备量很大。

而且相比消息队列,MQTT 的主题不需要提前创建,在订阅或发布时即自动的创建了主题,开发者无需再关心主题的创建,并且也不需要手动删除主题。

优点

  • 延迟很低
  • 可自定义可靠性 QoS
  • 支持大量设备连接
  • 适合资源受限的设备使用
  • 报文长度小,通信开销低

缺点

  • 吞吐量不如狭义上的消息队列中间件

总结

MQTT 也是基于 broker 架构的,但是定位是一个通信协议,但和狭义上的消息队列中间件算是殊途同归,但该通信协议比较轻量级,报文长度小,支持海量设备连接,适用于物联网场景。有时 MQTT 和消息队列会一起使用,MQTT 专注于处理物联网设备上报的数据,然后消息队列订阅这些消息并转发到不同的业务系统进行处理。而且 RabbitMQ 也内置了 MQTT 插件,来实现 MQTT 功能。

ZeroMQ

严格来说 ZeroMQ 是一个基于消息队列模型的网络库,它并不一定需要其他消息队列的 broker 中间件,非中心化架构,提供了 PUSH-PULL、PUB-SUB、Router-Dealer 等模式。使用起来很像 Socket。

优点

  • 去中心化,减少单点故障
  • 配置简单
  • 灵活性高
  • 点对点,低延迟高吞吐量
  • 跨平台

缺点

  • 复杂功能依赖程序员自己实现
  • 不保证消息持久化
  • 有些模式下消息顺序无法保证

总结

非中心化的基于消息队列模型的网络库,灵活,性能高,但使用负责,需要额外手段保证消息持久化,适用于简单的高性能传输场景,也可以根据自己的需求灵活实现相关功能。

基于 Redis 的消息队列

Redis 作为一款内存 KV 存储系统也可以作为轻量级的消息队列使用,可以基于两种方案实现:

  1. 发布订阅模式
  2. List
  3. Stream

发布订阅模式无法提供持久化,消息发送后就会销毁,消费端如果掉线后消息就丢失了,而且无法处理消息积压。


List 数据接口可以当作队列来使用,将其作为消息队列的原理如下:

  • 消息保序:使用 LPUSH + RPOP;
  • 阻塞读取:使用 BRPOP;
  • 重复消息处理:生产者自行实现全局唯一 ID;
  • 消息的可靠性:使用 BRPOPLPUSH
    但缺点是不支持多个消费者消费同一条消息,一旦消费者拿到一条消息后,这条消息就从 List 中删除了,无法被其它消费者再次消费。而且由于是内存型数据库,如果消息堆积过多会导致无法写入数据(如果采用了淘汰策略会导致数据丢失)

而 Redis 5.0 提供了 Stream 这个数据结构就是专门为消息队列设计的,其提供了消费组的概念可以实现多个消费者消费,具体可以参考 Redis 常见数据类型和应用场景 | 小林coding

1736349161143.png
图片源于 把Redis当作队列来用,真的合适吗?-腾讯云开发者社区-腾讯云

优点

  1. 简单、轻量级
  2. 易部署
  3. 延迟低

对于RabbitMQ和Redis的入队和出队操作,各执行100万次,每10万次记录一次执行时间。测试数据分为128Bytes、512Bytes、1K和10K四个不同大小的数据。实验表明:入队时,当数据比较小时Redis的性能要高于RabbitMQ,而如果数据大小超过了10K,Redis则慢的无法忍受;出队时,无论数据大小,Redis都表现出非常好的性能,而RabbitMQ的出队性能则远低于Redis。

缺点

  • 不能保证消息完全不丢失
  • 内存存储,空间资源紧张,难以应对大量消息积压场景
  • 功能较为简陋

总结

Redis 可以作为一个轻量级的消息队列使用,适用于简单业务,对数据丢失不敏感,且消息积压概率比较低的场景

Disruptor

本质是一个开源的系统内部的高性能内存队列,而不是分布式队列,用于线程间的数据传递,主要解决了 JDK 内置线程安全队列的性能和内存安全问题。提供了一个无锁有界的队列,相比内置队列性能更高。具体可以参考高性能队列——Disruptor - 美团技术团队

参考资料
消息队列基础知识总结 | JavaGuide
Redis 常见数据类型和应用场景 | 小林coding
把Redis当作队列来用,真的合适吗?-腾讯云开发者社区-腾讯云
# Kafka、RabbitMQ、RocketMQ 之间的区别是什么 ?
高性能队列——Disruptor - 美团技术团队