前几天遇到一个功能,就是小程序中促销发放优惠卷,用户抢到优惠卷,需要24小时内完成核销,如果过期未核销的优惠卷将被收回放到库存中。想到常见的3种实现方式:
1、使用Quartz,创建一个定时任务,到期后执行业务代码;
2、rabbitMq中的延迟队列;
3、对Redis的Key进行监控;
定时任务考虑暂时先不用了,系统有好多定时器,由于没什么并发量所以也不用mq消息队列了,想到使用rediskey过期监听事件通知来完成此功能比较适合。
redis自从2.8版本后加入了Keyspace Notifications功能,就是当我们对redis缓存key设置过期时间后,会触发Redis的键事件通知,客户端订阅这个通知,服务端将会把对应的通知事件发送给客户端,客户端收到通知,然后根据自己的不同业务进行处理。
A> 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
B> 添加redis配置
spring.redis.host=127.0.0.1
spring.redis.password=123456
spring.redis.port=6379
spring.redis.database=1
C> 修改Redis的redis.conf
notify-keyspace-events "Ex"
D> 添加Redis 消息监听的配置
@Configuration
public class RedisKeyExpirationListenerConfig {
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
E> 添加Redis key过期事件的监听
@Component
@Slf4j
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
public void onMessage(Message message, byte[] pattern){
String expiredKey = message.toString();
log.info("redis key过期:{}",expiredKey);
//TODO
}
}
需要注意的是:Redis的发布订阅模式采用的是发送即忘的策略,当订阅的客户端断线时,会丢失所有在断线期间发送给它的事件通知。如果程序对事件的可靠性要求高可用的话,Redis的键空间通知就不太适合了,可以考虑mq延迟队列+补偿机制。