目录
- Redis 为什么能够作为消息队列
- 三种消息队列实现方式概览
- Redis List 队列机制及 Spring 实战
- Redis Pub/Sub 发布订阅机制及使用方式
- Redis Stream:最强队列机制(含 ACK、消费组)
- Spring Boot 整合 Stream(完整可运行)
- Redis Stream 与 Kafka 的特点对比
- 使用场景总结
1. Redis 为什么可以用作消息队列
Redis 是一个基于内存的高性能 Key-Value 数据库,它的数据结构丰富且操作均为 O(1)。
队列的本质是"先进先出,按顺序取出",而 Redis 的 List、Pub/Sub 与 Stream 结构分别可以满足不同层次的队列需求:
- List:可以充当普通队列
- Pub/Sub:提供实时的发布订阅
- Stream:提供带持久化、消费组、ACK 确认机制的专业队列能力
这让 Redis 在轻量级 MQ 场景下非常灵活,也非常高效。
2. Redis 三种消息队列方案对比
| 方案 | 持久化 | 是否丢消息 | 是否支持消费组 | 是否需要 ACK | 使用难度 | 适用场景 |
|---|---|---|---|---|---|---|
| List(LPUSH/RPOP) | 支持 | 有可能丢失 | 不支持 | 不支持 | 最简单 | 简单后台任务、异步执行 |
| Pub/Sub | 不支持 | 订阅者离线即丢失 | 不支持 | 不支持 | 中等 | 实时消息推送、广播通知 |
| Stream | 支持 | 不容易丢失 | 支持 | 支持 | 相对复杂 | 订单处理、任务调度、分布式队列 |
从功能完善度来看,Stream 是最推荐的方案。
从使用简单度来看,List 是最容易上手的。
3. Redis List 队列机制(LPUSH + RPOP)
List 是 Redis 最经典的队列实现方式。通过"左进右出"就可以模拟一个典型的 FIFO 队列。
特点说明
- 实现简单,可快速上手
- 使用 RedisTemplate 就能轻松实现
- 不支持消费者确认机制,消息投递后无法保证成功处理
- 多消费者会导致竞争同一条消息,不适合任务分布式分发
因此,List 更适合简易异步任务、低可靠场景。
生产者示例:
java
@Autowired
private StringRedisTemplate redisTemplate;
public void sendMessage(String msg) {
redisTemplate.opsForList().leftPush("task_queue", msg);
}
消费者示例:
java
@Scheduled(fixedDelay = 1000)
public void consumer() {
String msg = redisTemplate.opsForList().rightPop("task_queue");
if (msg != null) {
System.out.println("处理任务:" + msg);
}
}
这种方式虽然简单,但不具备强队列能力。
4. Redis Pub/Sub 发布订阅模式
Pub/Sub 提供了一种非常实时化的消息推送方式,它不存储历史消息,只有在线订阅者才能收到。
模式特点
- 适合实时通知,如系统消息、广播消息、在线聊天
- 不会保存消息,订阅者掉线即丢失
- 没有 ACK,无法保证每条消息被处理
Pub/Sub 不适合作为任务队列,但适合"实时推送"。
监听器配置
java
@Configuration
public class RedisConfig {
@Bean
MessageListenerAdapter listenerAdapter(MessageReceiver receiver) {
return new MessageListenerAdapter(receiver, "onMessage");
}
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory factory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
container.addMessageListener(listenerAdapter, new PatternTopic("notice"));
return container;
}
}
消费者
java
@Component
public class MessageReceiver {
public void onMessage(String message) {
System.out.println("收到通知:" + message);
}
}
生产者
java
redisTemplate.convertAndSend("notice", "系统通知:服务器即将维护");
5. Redis Stream:专业级消息队列方案
Stream 是 Redis 5.0 引入的全新数据结构,是一套完整的消息队列系统,特点如下:
- 支持持久化,数据不会因断电丢失
- 支持消费者组,可由多个消费者组成消费集群
- 支持 ACK 确认机制,确保消息不会丢失
- 单条消息被确定处理后自动从 Pending 列表中移除
- 支持自动或手动 Claim,保证消息不会被遗忘
- 顺序性和性能都非常优秀
Stream 可以被认为是小型版的 Kafka,但更轻量、更容易部署。
6. Spring Boot 整合 Redis Stream
1)依赖引入
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2)初始化消息队列的消费组
为了防止不存在的消费组导致报错,一般在项目启动时创建消费组:
java
@Component
public class StreamInit {
@Autowired
private StringRedisTemplate redisTemplate;
@PostConstruct
public void init() {
try {
redisTemplate.opsForStream().createGroup("order_stream", "order_group");
} catch (Exception ignored) {}
}
}
3)生产者发送消息
java
@Autowired
private StringRedisTemplate redisTemplate;
public void sendOrder(String orderNo) {
Map<String, String> map = new HashMap<>();
map.put("orderNo", orderNo);
redisTemplate.opsForStream().add("order_stream", map);
}
4)消费者处理消息(包含 ACK)
java
@Component
public class OrderConsumer implements StreamListener<String, MapRecord<String, String, String>> {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public void onMessage(MapRecord<String, String, String> record) {
String orderNo = record.getValue().get("orderNo");
System.out.println("正在处理订单:" + orderNo);
redisTemplate.opsForStream().acknowledge("order_stream", "order_group", record.getId());
}
}
5)注册 Stream 监听器
java
@Configuration
public class StreamConfig {
@Bean
public RedisMessageListenerContainer listenerContainer(
RedisConnectionFactory factory,
OrderConsumer consumer) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, MapRecord<String, String, String>> options =
StreamMessageListenerContainer.StreamMessageListenerContainerOptions
.builder()
.pollTimeout(Duration.ofSeconds(2))
.build();
StreamMessageListenerContainer<String, MapRecord<String, String, String>> streamContainer =
StreamMessageListenerContainer.create(factory, options);
streamContainer.receiveAutoAck(
Consumer.from("order_group", "consumer_1"),
StreamOffset.create("order_stream", ReadOffset.lastConsumed()),
consumer
);
streamContainer.start();
return container;
}
}
至此,Redis Stream 的生产-消费整体链路完整实现,可以直接在实际项目中使用。
7. Redis Stream 与 Kafka 的对比分析
| 特点 | Redis Stream | Kafka |
|---|---|---|
| 吞吐量 | 中等偏高 | 极高 |
| 消费机制 | 消费组 + ACK | 消费组 + Offset |
| 持久化方式 | 内存主导 + AOF | 磁盘顺序写 |
| 依赖环境 | 极简,部署容易 | 较复杂,需要 Zookeeper 或 Kraft |
| 使用场景 | 中小型业务,订单流、异步任务 | 海量日志、实时流处理、大型数据系统 |
结论: 当系统规模较小时,Redis Stream 是简单高效的队列方案;当系统达到大规模吞吐需求时,Kafka 才是更合适的选择。
8. 使用场景总结
以下场景适合完全使用 Redis Stream 或 List/PubSub:
- 订单创建后的异步处理
- 秒杀请求异步落库
- 延迟任务队列
- 短信/邮件异步发送
- 系统通知、用户消息推送
- 日志或审计消息收集
- 数据同步、事件驱动架构
Redis 的灵活性使它能够同时承担缓存、分布式锁和消息队列等多种角色,适用于多种中小型分布式系统架构。
结语
Redis 不仅是一个高性能的缓存数据库,同时也是一个非常灵活且强大的轻量级消息队列解决方案。依托于其多样化的数据结构、极高的读写性能以及简单易部署的特点,Redis 在现代微服务架构中扮演着远超"缓存"本身的角色。在许多中小型项目中,它可以承担消息传递、事件驱动、异步任务处理等职责,甚至可以在一定程度上替代专业消息中间件。
在 Spring 体系中,Redis 的集成方式非常成熟且丰富,无论是基于 List 的简易队列、基于 Pub/Sub 的实时广播消息,还是基于 Stream 的分布式消息队列,都可以通过 Spring Data Redis 顺畅地接入应用系统。其中 Redis Stream 是目前最值得推荐的方案,它具备消息持久化、消费者组、消息确认机制(ACK)、Pending 列表管理、顺序性保证等能力,能够提供接近 Kafka 那样的消息投递可靠性,同时又保持了 Redis 一贯的简单部署和轻量成本。