You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot对接3节点Redis集群:过期事件不触发问题排查

Redis集群下key过期事件无法接收的排查与解决

针对你遇到的Redis集群(3节点)中手动发布过期事件能收到,但实际key过期无通知的问题,可按以下步骤排查解决:


1. 确保集群所有节点都配置了过期事件通知

Redis集群中,key分布在不同节点,只有key所在的节点会发送对应的过期事件。如果仅配置了单个节点,其他节点的key过期不会触发通知:

  • 登录每个Redis节点,执行命令:
    CONFIG SET notify-keyspace-events Ex
    
  • 修改每个节点的redis.conf文件,添加配置:
    notify-keyspace-events Ex
    
  • 重启节点后,在每个节点执行CONFIG GET notify-keyspace-events,确认返回结果均为"Ex"。

2. 检查Redis过期键删除策略

Redis默认采用惰性删除+定期删除策略:

  • 惰性删除:只有当key被访问时才会检查是否过期并删除
  • 定期删除:Redis每隔一段时间(由hz参数控制,默认10次/秒)随机扫描部分key
    如果key过期后未被访问,且未被定期扫描到,不会立即触发过期事件。
  • 测试时可临时调高hz参数(如改为100),提高扫描频率,修改redis.conf后重启节点(生产环境需评估CPU占用影响):
    hz 100
    

3. 修复Spring Boot的集群订阅配置

Lettuce连接Redis集群时,默认共享连接只会连接单个节点,导致无法接收其他节点的事件。需调整RedisMessageListenerContainer配置:
修改CacheConfig中的redisContainer Bean,禁用共享连接:

@Bean
public RedisMessageListenerContainer redisContainer(RedisConnectionFactory connectionFactory,
                                                    RedisExpirationListenerService listener) {
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    
    // 集群环境下禁用共享连接,确保订阅所有节点的事件
    if (connectionFactory instanceof LettuceConnectionFactory lettuceConnFactory) {
        lettuceConnFactory.setShareNativeConnection(false);
    }
    
    container.addMessageListener(new MessageListenerAdapter(listener), new PatternTopic("__keyevent@*__:expired"));
    log.info("Redis expiration listener subscribed to __keyevent@*__:expired");
    return container;
}

解释:禁用共享连接后,Spring会为每个节点创建独立的订阅连接,从而接收集群所有节点的过期事件。

4. 验证key的存储与过期状态

  • 确认测试key的完整名称:如果使用Spring Cache,key会带有配置的前缀(如checkout::cacheName::your-key),需用完整key测试。
  • 找到key所在节点:执行CLUSTER KEYSLOT "full-key-name"获取槽位,再执行CLUSTER GETKEYSINSLOT 槽位值 10查看该槽位的key,确认key存在。
  • 检查过期时间:执行TTL "full-key-name",返回-2表示key已被删除,此时应触发事件。

5. 修正监听器的消息解析逻辑

避免message.toString()可能存在的编码问题,改为直接解析消息体字节数组:

@Service
@Slf4j
public class RedisExpirationListenerService implements MessageListener {

    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expiredKey = new String(message.getBody(), StandardCharsets.UTF_8);
        log.info("Redis key expired: {}", expiredKey);

        if (expiredKey.endsWith("_dsm")) {
            log.info("Triggering call to expire slot for: {}", expiredKey);
            //triggerApiCall(expiredKey);
        }
    }
}

内容的提问来源于stack exchange,提问作者Lucifer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 07:38:15