点击上方“Java精选”,选择“设为星标”
别问别人为什么,多问自己凭什么!
下方有惊喜,留言必回,有问必答!
每天 08:15 更新文章,每天进步一点点...
我们以订单功能为例说明下:
生成订单后一段时间不支付订单会自动关闭。最简单的想法是设置定时任务轮询,但是每个订单的创建时间不一样,定时任务的规则无法设定,如果将定时任务执行的间隔设置的过短,太影响效率。
还有一种想法,在用户进入订单界面的时候,判断时间执行相关操作。方式可能有很多,在这里介绍一种监听 Redis 键值对过期时间来实现订单自动关闭。
在生成订单时,向 Redis 中增加一个 KV 键值对,K 为订单号,保证通过 K 能定位到数据库中的某个订单即可,V 可为任意值。
假设,生成订单时向 Redis 中存放 K 为订单号,V 也为订单号的键值对,并设置过期时间为 30 分钟,如果该键值对在 30 分钟过期后能够发送给程序一个通知,或者执行一个方法,那么即可解决订单关闭问题。
推荐下自己做的 Spring boot 的实战项目:
https://gitee.com/yoodb/jing-xuan
实现:通过监听 Redis 提供的过期队列来实现,监听过期队列后,如果 Redis 中某一个 KV 键值对过期了,那么将向监听者发送消息,监听者可以获取到该键值对的 K,注意,是获取不到 V 的,因为已经过期了,这就是上面所提到的,为什么要保证能通过 K 来定位到订单,而 V 为任意值即可。拿到 K 后,通过 K 定位订单,并判断其状态,如果是未支付,更新为关闭,或者取消状态即可。
修改 redis 相关事件配置。找到 redis 配置文件 redis.conf,查看 notify-keyspace-events 配置项,如果没有,添加 notify-keyspace-events Ex,如果有值,则追加 Ex,相关参数说明如下:
K
:keyspace 事件,事件以 keyspace@ 为前缀进行发布
E
:keyevent 事件,事件以 keyevent@ 为前缀进行发布
g
:一般性的,非特定类型的命令,比如del,expire,rename等
$
:字符串特定命令
l
:列表特定命令
s
:集合特定命令
h
:哈希特定命令
z
:有序集合特定命令
x
:过期事件,当某个键过期并删除时会产生该事件
e
:驱逐事件,当某个键因 maxmemore 策略而被删除时,产生该事件
A
:g$lshzxe的别名,因此”AKE”意味着所有事件
在 pom.xml 中添加 org.springframework.boot:spring-boot-starter-data-redis 依赖
org.springframework.boot
spring-boot-starter-data-redis
定义配置 RedisListenerConfig 实现监听 Redis key 过期时间
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
@Configuration
public class RedisListenerConfig {
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
定义监听器 RedisKeyExpirationListener,实现KeyExpirationEventMessageListener
接口,查看源码发现,该接口监听所有 db 的过期事件 keyevent@*:expired"
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;
/**
* 监听所有db的过期事件__keyevent@*__:expired"
*/
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
/**
* 针对 redis 数据失效事件,进行数据处理
* @param message
* @param pattern
*/
@Override
public void onMessage(Message message, byte[] pattern) {
// 获取到失效的 key,进行取消订单业务处理
String expiredKey = message.toString();
System.out.println(expiredKey);
}
}
作者:彭楷淳
https://antoniopeng.com/
公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!
------ THE END ------
>Java精选面试题<
3000+ 道面试题在线刷,最新、最全 Java 面试题!
期往精选 点击标题可跳转
【205期】Mysql 的管理工具 Sequel Pro,界面简洁易用!
【206期】请求接口合并的三种方式,大大提高接口性能!
【207期】阿里二面:main 方法可以继承吗?
【208期】面试官问:Redis 分布式锁如何自动续期?
【209期】图解用户登录验证业务流程(面试应答推荐)
【210期】Spring Boot 多线程环境下,解决多个定时器冲突问题
【211期】 4 种延时任务实现方案
【212期】 架构师经常提起领用 DNS 解析实现负载均衡是个什么“鬼”?
最近有很多人问,有没有读者&异性交流群,你懂的!想知道如何加入。加入方式很简单,有兴趣的同学,只需要点击下方卡片,回复“加群”,即可免费加入交流群!
文章有帮助的话,在看,转发吧!