上一篇我们聚焦Redis在电商核心场景的实战应用,通过商品缓存、库存扣减、分布式锁解决了高并发下的性能与数据一致性问题。而在电商、支付、日志同步等业务中,消息队列是解耦服务、异步削峰的关键组件,Redis凭借轻量、易用、高性能的特性,成为中小型业务和快速落地场景的首选MQ方案。

Redis提供了三种消息队列实现方案:List(列表)、Pub/Sub(发布订阅)、Stream(流),三者适用场景、可靠性、功能特性差异极大。本篇从核心原理、实战代码、优缺点分析到生产选型,全方位拆解三种方案,帮你在业务中精准选择合适的Redis消息队列。

核心定位:Redis MQ主打轻量快捷,无需部署独立MQ中间件,List适合简单队列,Pub/Sub适合广播场景,Stream是生产级可靠消息方案。

一、方案一:基于List实现简单消息队列

List是Redis基础数据结构,通过LPUSH(生产者入队)+ BRPOP(消费者阻塞出队)实现轻量级队列,是Redis最原始、最简单的MQ实现,适合无严格可靠性要求的简单异步场景。

1. 核心原理

  • 生产者用LPUSH将消息推入队列左侧,消费者用BRPOP阻塞式从队列右侧拉取消息
  • 支持消息持久化(依赖Redis RDB/AOF),重启后消息不丢失
  • 单消费者模式,多消费者会出现消息争抢,无法实现广播或分组消费

2. 实战代码(Java)

/**
 * List队列Key
 */
private static final String LIST_MQ_KEY = "mq:list:order";

// 生产者:消息入队
public void listProducer(String msg) {
    redisTemplate.opsForList().leftPush(LIST_MQ_KEY, msg);
}

// 消费者:阻塞拉取消息(后台线程持续监听)
@Async
public void listConsumer() {
    while (true) {
        // 阻塞超时时间:5秒,避免空轮询
        String msg = redisTemplate.opsForList().rightPop(LIST_MQ_KEY, 5, TimeUnit.SECONDS);
        if (StringUtils.isNotBlank(msg)) {
            // 处理业务逻辑
            handleMsg(msg);
        }
    }
}

3. 优缺点分析

  • ✅ 优点:实现极简、性能极高、支持持久化、无额外依赖
  • ❌ 缺点:不支持ACK确认、无消费重试、不支持分组/广播、消息无法重复消费

4. 适用场景

日志异步记录、简单通知推送、非核心业务异步处理,对消息可靠性要求低的场景。


二、方案二:基于Pub/Sub实现发布订阅模式

Pub/Sub是Redis专属的发布订阅模型,生产者(发布者)发送消息到频道,多个消费者(订阅者)可同时接收消息,实现广播消费,但完全不持久化,适合实时性高、允许消息丢失的场景。

1. 核心原理

  • 发布者通过PUBLISH向频道发送消息,订阅者通过SUBSCRIBE监听频道
  • 消息不存储、不持久化,订阅者离线期间的消息会直接丢失
  • 支持多订阅者广播,一个消息可被多个消费者同时消费

2. 实战代码(Java)

/**
 * Pub/Sub频道名
 */
private static final String PUBSUB_CHANNEL = "mq:channel:notice";

// 生产者:发布消息
public void pubProducer(String msg) {
    redisTemplate.convertAndSend(PUBSUB_CHANNEL, msg);
}

// 消费者:订阅频道(配置类注册监听器)
@Component
public class PubSubConsumer extends MessageListenerAdapter {
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String msg = new String(message.getBody());
        // 处理广播消息
        handleBroadcastMsg(msg);
    }
}

3. 优缺点分析

  • ✅ 优点:支持广播消费、延迟极低、实现简单、多消费者实时同步
  • ❌ 缺点:无持久化、消息不可靠、离线丢消息、无ACK、不支持重试

4. 适用场景

实时通知、缓存刷新广播、配置推送、聊天室消息,允许少量消息丢失的实时场景。


三、方案三:基于Stream实现生产级可靠消息队列

Stream是Redis 5.0新增的专门用于消息队列的数据类型,借鉴了Kafka的设计理念,支持消息持久化、ACK确认、消费分组、偏移量重置、消息回溯,是Redis官方推荐的生产级MQ方案,完美弥补List和Pub/Sub的缺陷。

1. 核心特性

  • 消息持久化存储,支持历史消息回溯
  • 消费分组(Consumer Group),支持负载均衡和广播两种模式
  • ACK确认机制,消费失败可重试,避免消息丢失
  • 自动生成唯一消息ID,支持按偏移量消费

2. 实战代码(Java+消费分组)

/**
 * Stream队列Key、消费组名
 */
private static final String STREAM_MQ_KEY = "mq:stream:order";
private static final String STREAM_GROUP = "order-group";
private static final String STREAM_CONSUMER = "order-consumer-01";

// 初始化消费组(项目启动时执行一次)
@PostConstruct
public void initStreamGroup() {
    try {
        redisTemplate.opsForStream().createGroup(STREAM_MQ_KEY, STREAM_GROUP);
    } catch (Exception e) {
        // 消费组已存在,忽略异常
    }
}

// 生产者:发送消息
public void streamProducer(String orderNo, String userId) {
    MapRecord<String, String, String> record = Record.of(Map.of("orderNo", orderNo, "userId", userId));
    redisTemplate.opsForStream().add(STREAM_MQ_KEY, record);
}

// 消费者:分组消费+ACK确认
@Async
public void streamConsumer() {
    while (true) {
        // 阻塞拉取消息,每次拉取1条,超时5秒
        List<MapRecord<String, String, String>> records = redisTemplate.opsForStream()
                .read(Consumer.from(STREAM_GROUP, STREAM_CONSUMER),
                        StreamReadOptions.empty().block(Duration.ofSeconds(5)).count(1),
                        StreamOffset.create(STREAM_MQ_KEY, ReadOffset.lastConsumed()));
        if (CollUtil.isNotEmpty(records)) {
            for (MapRecord<String, String, String> record : records) {
                try {
                    // 处理业务逻辑
                    handleOrderMsg(record.getValue());
                    // ACK确认:标记消息已消费
                    redisTemplate.opsForStream().acknowledge(STREAM_MQ_KEY, STREAM_GROUP, record.getId());
                } catch (Exception e) {
                    // 消费失败,记录日志,等待重试
                    log.error("消息消费失败,ID:{}", record.getId(), e);
                }
            }
        }
    }
}

3. 优缺点分析

  • ✅ 优点:生产级可靠、持久化、ACK确认、消费分组、消息回溯、负载均衡
  • ❌ 缺点:相比List/Pub/Sub稍重、学习成本略高、低版本Redis不支持

4. 适用场景

订单异步处理、支付结果通知、库存同步、日志持久化采集,对消息可靠性有要求的核心业务。


四、三种Redis消息队列全方位对比

对比维度List(列表)Pub/Sub(发布订阅)Stream(流)
持久化支持(依赖RDB/AOF)不支持,离线丢消息完整持久化,可回溯
ACK确认不支持不支持支持,消费更可靠
消费模式单消费者,争抢模式多消费者,广播模式分组负载+广播双模式
消息丢失极低(重启可恢复)极高,离线必丢几乎不丢失
复杂度极低中等
生产适用性简单非核心场景实时广播场景核心业务,生产首选

选型口诀:简单异步用List,实时广播选Pub/Sub,核心可靠上Stream,Redis MQ按需选,轻量快捷不复杂。

五、生产环境选型建议与避坑

1. 精准选型指南

  • 中小型项目、非核心异步任务:选用List,快速落地
  • 多服务广播通知、实时推送:选用Pub/Sub,极低延迟
  • 核心业务、订单/支付/库存同步:选用Stream,生产级可靠

2. 生产避坑要点

  • List禁止用于核心业务,无ACK机制存在消息丢失风险
  • Pub/Sub严禁用于订单、支付等不能丢消息的场景
  • Stream需合理设置消息过期时间,避免内存暴涨
  • 高吞吐大流量场景,优先选用专业MQ(Kafka/RocketMQ)

结语

Redis消息队列是轻量异步解耦的利器,三种方案各有所长,没有绝对的优劣,只有场景的适配。掌握List、Pub/Sub、Stream的特性与选型,既能快速落地业务,又能避免消息丢失、性能瓶颈等问题。

发表回复