Spring Boot Redis发布订阅配置RedisMessageListenerContainer

作者:技术拾光时间:2026-07-20 08:37:02浏览:436

RedisMessageListenerContainer需手动声明为Bean,监听器必须实现MessageListener接口并通过addMessageListener()方法注册。为保证数据正确,序列化方式需与发布端完全一致,推荐统一使用StringRedisTemplate。发布端调用convertAndSend()发送消息时,需注意异常处理,例如添加

RedisMessageListenerContainer 得自己手动声明 Bean,Spring Boot 2.0+ 不会自动帮你配好;监听器要实现 MessageListener,通过 addMessageListener() 手工注册,别指望 @EventListener 能管用;序列化必须跟发布端保持一致,最省心的做法是统一用 StringRedisTemplate。

Spring Boot Redis发布订阅配置RedisMessageListenerContainer

手动声明 RedisMessageListenerContainer Bean 是 Spring Boot 2.0+ 的必备配置

Spring Boot 2.0+ 默认不会自动创建 RedisMessageListenerContainer——即使你已经正确引入了 spring-boot-starter-data-redis 并配置好了 RedisConnectionFactory,它依然不会自动生效。如果没有显式地声明一个 Bean,那么 @EventListener 或者你自己编写的监听器类都不会被触发,订阅逻辑就会静默失效。

一个典型的踩坑现场:启动日志中看不到 “Starting RedisMessageListenerContainer” 这句话,消息发送后监听器毫无反应,也没有报错,就像石沉大海。

正确做法很简单:手动声明一个 Bean,并确保它依赖已经配置好的 RedisConnectionFactory:

@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
        RedisConnectionFactory connectionFactory) {
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    container.setTaskExecutor(new ThreadPoolTaskExecutor()); // 别让主线程卡住
    return container;
}

使用 addMessageListener() 注册监听器,而非 @EventListener

Spring 的 @EventListener 仅适用于 Spring 自身的事件体系(ApplicationEvent),对 Redis 的发布/订阅消息完全无效。你必须通过 RedisMessageListenerContainer.addMessageListener() 将监听器实例显式地挂载上去。

一个容易掉入的误区:编写一个带有 @Component 和 @EventListener 的类,误以为这样就能接收 Redis 消息——实际上永远收不到。

实操建议:

  • 监听器要实现 MessageListener 接口(或者继承 KeyExpirationEventMessageListener 等子类)
  • 使用 PatternTopic 订阅通配符频道(例如 PatternTopic("user:*")),使用 ChannelTopic 订阅精确频道(例如 ChannelTopic("user.login"))
  • 注册时传入监听器实例和 Topic 列表,例如:container.addMessageListener(myListener, new ChannelTopic("order.created"))

消息序列化不一致导致 onMessage() 收到 null 或乱码

默认情况下,RedisMessageListenerContainer 使用 JdkSerializationRedisSerializer。但如果生产者端使用的是 StringRedisTemplate(即 StringRedisSerializer),两边的序列化方式就会不匹配。onMessage(Message, byte[]) 中的 message.getBody() 会得到乱码,甚至直接是空字节数组。

解决方案取决于实际场景:

  • 统一使用 StringRedisTemplate:在配置容器时设置 setTopicSerializer(new StringRedisSerializer())(注意:此方法仅在 Spring Data Redis 2.6+ 中可用)
  • 降级兼容老版本:自行实现 MessageListener,在 onMessage() 中手动用 new String(message.getBody(), StandardCharsets.UTF_8) 解码
  • 避免混用:不要让 RedisTemplate 和 StringRedisTemplate 对同一组频道收发消息,否则会引发不必要的麻烦

发布端必须使用 RedisTemplate 或 StringRedisTemplate 的 convertAndSend()

切勿使用 execute() 配合原生的 PUBLISH 命令绕过序列化逻辑,否则监听器收到的消息体将无法反序列化——尤其是当容器配置了非默认序列化器时。

正确的发布方式只有两种:

  • redisTemplate.convertAndSend("channel.name", "hello") —— 前提是 redisTemplate 的 value serializer 与监听器的解码方式一致
  • stringRedisTemplate.convertAndSend("channel.name", "hello") —— 最简单无脑的方案,强烈推荐用于纯字符串消息

有一个容易忽略的细节:convertAndSend() 虽然是异步非阻塞的,但如果连接池耗尽或 Redis 服务宕机,它会抛出 RedisConnectionFailureException。因此外层最好捕获并处理异常,不要天真地认为“消息发出去就一定能到达”。