SpringBoot实现监听redis key失效事件
当用户创建订单后需在30分钟内支付,否则订单失效。
一、基于定时任务
对于这样的需求我们首先想到的是指定时间的定时任务,或者长轮询的定时查询订单是否失效。这样的方式也能实现但这种高效率的延迟任务用任务调度(定时器)实现就得不偿失,而且对系统也是一种压力且数据库消耗极大。
二、基于MQ的定时消息或延迟消息实现
定时消息:Producer 将消息发送到 MQ 服务端,但并不期望这条消息立马投递,而是推迟到在当前时间点之后的某一个时间投递到 Consumer 进行消费,该消息即定时消息。
延迟消息:Producer 将消息发送到 MQ 服务端,但并不期望这条消息立马投递,而是延迟一定时间后才投递到 Consumer 进行消费,该消息即延时消息。
定时消息与延迟消息在代码配置上存在一些差异,但是最终达到的效果相同:消息在发送到 MQ 服务端后并不会立马投递,而是根据消息中的属性延迟固定时间后才投递给消费者。
目前业界MQ对定时消息和延迟消息的支持情况:
可以看见不同的MQ对定时任务支持方式不同,而且技术复杂度和成本提高。
三、使用Redis过期Key监听实现
利用redis的key自动过期机制,再下单时将订单id写入redis,过期时间30分钟,监听key过期事件进行支付超时处理。
这种方式处理起来简单的很,对业务量不大的公司来说完全可以接受。
存在的弊端:
- 1.后台服务不可用时,如果此时有key失效那么是监听不到的。(解决方法:项目启动时或定时任务补偿处理)
- 2.redis重启或不可用时,导致业务无法处理。(解决方法:redis集群实现高可用)
1、Redis过期事件
通过订阅与发布功能(pub/sub)来进行分发。而对超时的监听呢,并不需要自己发布,只有修改配置文件redis.conf中的:notify-keyspace-events Ex,默认为notify-keyspace-events “”
1 | K 键空间通知,以__keyspace@<db>__为前缀 |
打开一个redis-cli ,监控db0的key过期事件
1 | 127.0.0.1:6379> PSUBSCRIBE __keyevent@0__:expired |
打开另一个redis-cli ,发送定时过期key
1 | 127.0.0.1:6379> setex test_key 3 test_value |
观察上一个redis-cli ,会发现收到了过期的key test_key,但是无法收到过期的value test_value
1 | 127.0.0.1:6379> PSUBSCRIBE __keyevent@0__:expired |
2、集成SpringBoot中使用
首先要添加POM依赖
1 | <dependency> |
实现方式一:使用监听器,该接口监听所有db的过期事件keyevent@*:expired
- RedisListenerConfig
1 | package cn.pconline.config.redis; |
- RedisKeyExpirationListener
1 | package cn.pconline.config.redis; |
实现方式二:自定义监听器,这个地方定义的比较灵活,可以自己定义监控什么事件。
- RedisListenerConfig
1 | package cn.pconline.config.redis; |
- RedisExpiredListener
1 | package cn.pconline.config.redis; |