Redis消息监听必须配置RedisMessageListenerContainer,它负责订阅、连接维持和反序列化;未配置setConnectionFactory会启动失败;PatternTopic需Redis开启K事件;MessageListenerAdapter必须显式调用afterPropertiesSet();序列化需两端一致,推荐GenericJackson2JsonRedisSerializer;Pub/Sub是广播模型,多实例均会收到消息。

Redis消息监听必须配RedisMessageListenerContainer
没这个容器,MessageListener接口写了也白写——它只是个回调契约,真正拉起订阅、维持连接、反序列化消息的,全靠RedisMessageListenerContainer。Spring Boot不会自动帮你注册它,必须手动声明为@Bean。
- 不配置
setConnectionFactory()会直接启动失败,报NullPointerException在初始化阶段 - 一个容器可绑定多个
MessageListener,但别试图复用同一个MessageListener实例监听多个频道——状态可能串扰 - 如果用
PatternTopic(比如new PatternTopic("order:*")),注意Redis服务端需开启notify-keyspace-events中的K标志,否则匹配不到
MessageListenerAdapter的afterPropertiesSet()不能省
这是最容易被跳过的一步:哪怕你用@Component把监听器交给Spring管理,只要没显式调用adapter.afterPropertiesSet(),容器启动时不会报错,但运行时收不到任何消息,日志里只有一句静默的ERROR o.s.d.r.listener.adapter.MessageListenerAdapter - Listener execution failed。
- 正确姿势是在
@Bean方法里创建MessageListenerAdapter后立刻调用它 - 如果你用
MessageListenerAdapter指定非默认方法名(比如"handle"而非"onMessage"),得确保目标方法签名是void handle(Message, byte[]),参数顺序不能错 - 别依赖Lombok的
@AllArgsConstructor去注入RedisTemplate到监听器里——监听器由容器反射创建,不是Spring托管Bean,字段会是null
消息体反序列化要和发布端严格对齐
发布端用redisTemplate.convertAndSend("topic", obj),订阅端message.getBody()拿到的是字节数组,怎么转回来?取决于RedisTemplate配置的valueSerializer。默认是JdkSerializationRedisSerializer,但生产环境几乎没人用——它要求类路径一致、版本一致、还慢。
- 推荐统一设为
GenericJackson2JsonRedisSerializer,发布/订阅两端都这么配,JSON兼容性好 - 如果发布端用
StringRedisTemplate发字符串,订阅端就不能用RedisTemplate<string object></string>直接接收,否则getBody()解出来是乱码或空对象 - 遇到
Could not read JSON: Can not construct instance of XXX,八成是JSON字段名和Java属性名不匹配,加@JsonProperty或调整ObjectMapper的命名策略
多实例部署时,每个实例都会收到同一条消息
Redis的Pub/Sub是广播模型,不是队列模型。10个服务实例监听user.register,发一次消息,10个都会触发onMessage。这不是Bug,是设计如此。
- 如果业务需要“仅一个实例处理”,得自己加分布式锁(比如用
SET key value NX EX 30) - 如果只是日志、缓存刷新这类幂等操作,可以不管;但涉及扣库存、发短信,就必须判重或加锁
- 别指望
RedisMessageListenerContainer提供“消费确认”或“重试机制”——它没有ACK概念,消息丢了就是丢了
最常被忽略的其实是序列化一致性——开发时本地跑通,上线后因RedisTemplate配置不一致,消息一发就变null,排查起来特别绕。










