lpush+brpop构成fifo阻塞队列,兼容redis 2.0+;但消费失败会导致消息丢失,适合允许少量丢失的场景,强可靠性需求应改用stream。

直接用 LPOP + RPUSH 做队列容易丢消息
很多人一上来就写 jedis.lpush("queue", msg) 和 jedis.lpop("queue"),看起来顺序对、API 简单,但生产环境会出问题:如果消费者在 LPOP 取到消息后、处理中途崩溃(比如 JVM OOM、机器断电),这条消息就彻底消失了——LPOP 是“取且删”,没有重试或回滚机制。
真正可用的最小闭环是:BRPOP(阻塞式右弹出) + 手动确认逻辑。它能避免空轮询,也给处理失败留了余地。
-
BRPOP queue 5:最多等 5 秒,有消息就立刻返回,没消息就挂起线程,不耗 CPU - 拿到消息后,先
RPUSH queue_processing把它暂存到处理中队列(带时间戳或唯一 ID) - 处理成功 →
LREM queue_processing 1 msg清理;失败 → 等超时后由补偿任务扫表重投
@Scheduled 不适合做消费者主线程
用 @Scheduled(fixedDelay = 100) 轮询 LLEN 再 LPOP,看似可控,实则埋雷:
- 高并发下多个实例同时
LPOP,可能重复消费(Redis List 本身不保证分布式锁语义) - 轮询间隔设太小 → 频繁发命令,Redis QPS 暴涨;设太大 → 消息积压延迟升高
- Spring 的
@Scheduled是单机调度,集群部署时多个节点会各自拉取同一批消息
正确做法是让每个消费者独占一个连接,用 BRPOP 阻塞等待,配合线程池隔离处理逻辑。不要把它塞进 Spring 定时任务里。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
用 RedisTemplate 代替裸 Jedis 时注意序列化陷阱
Spring Boot 默认的 RedisTemplate 对 key/value 都用 JDK 序列化,导致 Redis 里全是乱码二进制,redis-cli 查不到、其他语言客户端读不了,调试极其痛苦。
必须显式配置序列化器:
@Bean
public RedisTemplate<string object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<string object> template = new RedisTemplate();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
</string></string>
- key 必须用
StringRedisSerializer,否则lrange myqueue 0 -1在命令行根本看不到数据 - value 推荐
GenericJackson2JsonRedisSerializer,不是StringRedisSerializer—— 后者只能存字符串,对象会变toString()结果,反序列化时类型信息全丢 - 别信自动配置的“开箱即用”,不改序列化器,你连自己存了什么都看不到
消息体里必须带唯一 ID 和时间戳
哪怕只是内部系统通信,也要在消息 payload 中硬编码 id 和 timestamp 字段:
public class TaskMessage {
private String id = UUID.randomUUID().toString();
private long timestamp = System.currentTimeMillis();
private String bizType;
private String payload;
}
- 没
id就没法做幂等:消费者重试时无法判断是不是同一消息 - 没
timestamp就没法做超时控制:处理中队列里的消息卡住几天没人管,也没法自动捞出来重投 - 别指望靠 Redis key 名称或 List 下标来识别消息——List 是无序插入的,
LPUSH和RPUSH混用会导致顺序错乱
Redis List 做队列不是不能用,而是它的“简单”背后藏着大量隐性约束。真正稳的方案,永远是从消息丢失、重复、乱序这三个最痛的点倒推设计,而不是抄一段 lpush/lpop 就上线。










