import com.shgbitai.manmachineability.constance.RedisKeyPrefix;
import com.shgbitai.manmachineability.service.RedisService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.ReactiveRedisMessageListenerContainer;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
@Slf4j
public class RedisKeyExpirationListenerConfig {
@Resource
private RedisService redisService;
@Bean
ReactiveRedisMessageListenerContainer keyExpirationListenerContainer(ReactiveRedisConnectionFactory connectionFactory, @Value("${spring.data.redis.database}") String databaseIndex) {
ReactiveRedisMessageListenerContainer container = new ReactiveRedisMessageListenerContainer(connectionFactory);
// 订阅过期事件。这里的"__keyevent@0__:expired"是监听默认数据库中Key过期事件的频道。
// 如果你的应用使用的不是默认数据库,需要更改这里的数字。
String expiredTopic = String.format("__keyevent@%s__:expired", 0);
container.receive(ChannelTopic.of(expiredTopic))
.doOnNext(message -> {
// 此处处理接收到的过期Key。message.getMessage()包含了过期的Key。
String expiredKey = message.getMessage();
log.info("会话过期的key: " + expiredKey);
//如果session聊天室会话时长过期,通知前端,会话关闭
if(expiredKey.contains(RedisKeyPrefix.SESSION_LAST_ACTIVITY_KEY_PREFIX)){
String sessionId = Optional.of(expiredKey)
.filter(s -> s.contains(":"))
.map(s -> s.substring(s.indexOf(":") + 1)).orElse("");
log.info("会话关闭,过期的会话SessionId:{}",sessionId);
redisService.setWebSocketSessionExpired(sessionId);
}
})
.subscribe();
return container;
}
}
监听 Redis key 过期事件无效的问题可能有几个原因。以下是一些可能的原因和相应的解决方案:
Redis 配置未启用过期事件通知:
默认情况下,Redis 可能没有启用键过期事件的通知。你需要在 Redis 的配置文件 redis.conf
中设置 notify-keyspace-events
选项,或者通过运行 CONFIG SET
命令动态设置。
Ex
(E 代表 Keyevent 事件,x 代表过期事件):CONFIG SET notify-keyspace-events Ex
redis.conf
中添加:notify-keyspace-events Ex
监听错误的数据库:
在你的代码中,String expiredTopic = String.format("__keyevent@%s__:expired", 0);
这一行硬编码了数据库索引为 0
。如果你的应用使用的不是数据库 0
,则需要修改这个索引以匹配你实际使用的数据库。你已经通过注解 @Value("${spring.data.redis.database}") String databaseIndex
获取了数据库索引,但是没有使用它来格式化 expiredTopic
。
String expiredTopic = String.format("__keyevent@%s__:expired", databaseIndex);
确保 Redis 版本支持:
确保你使用的 Redis 版本支持键空间通知。虽然大多数现代版本都支持,但是如果你使用的是较旧的版本,可能会有问题。
日志配置:
确保你的日志配置正确,以便可以看到 log.info
输出的相关信息。如果日志级别设置过高,可能导致这些信息未被记录。
检查连接工厂配置:
确保 ReactiveRedisConnectionFactory
正确配置并且能够与 Redis 服务器建立连接。
测试和调试:
调整上述设置后,你的应用应该能够正确监听和响应 Redis 键过期事件。如果问题依旧,请进一步检查网络配置及权限设置。