解决方案1:

可以利用redis天然的key自动过期机制,下单时将订单id写入redis,过期时间30分钟,30分钟后检查订单状态,如果未支付,则进行处理但是key过期了redis有通知吗?答案是肯定的。

开启redis key过期提醒

修改redis相关事件配置。找到redis配置文件redis.conf,查看“notify-keyspace-events”的配置项,如果没有,添加“notify-keyspace-events Ex”,如果有值,添加Ex,相关参数说明如下:

K:keyspace事件,事件以__keyspace@<db>__为前缀进行发布;
E:keyevent事件,事件以__keyevent@<db>__为前缀进行发布;
g:一般性的,非特定类型的命令,比如del,expire,rename等;
$:字符串特定命令;
l:列表特定命令;
s:集合特定命令;
h:哈希特定命令;
z:有序集合特定命令;
x:过期事件,当某个键过期并删除时会产生该事件;
e:驱逐事件,当某个键因maxmemore策略而被删除时,产生该事件;
A:g$lshzxe的别名,因此”AKE”意味着所有事件。
redis测试:

打开一个redis-cli ,监控db0的key过期事件

127.0.0.1:6379> PSUBSCRIBE __keyevent@0__:expired
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__keyevent@0__:expired"
3) (integer) 1

打开另一个redis-cli ,发送定时过期key

127.0.0.1:6379> setex test_key 3 test_value

观察上一个redis-cli ,会发现收到了过期的keytest_key,但是无法收到过期的value test_value

127.0.0.1:6379> PSUBSCRIBE __keyevent@0__:expired
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__keyevent@0__:expired"
3) (integer) 1
1) "pmessage"
2) "__keyevent@0__:expired"
3) "__keyevent@0__:expired"
4) "test_key"

在springboot中使用

  • 1.pom 中添加依赖
        <!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 2.定义配置RedisListenerConfig
import edu.zut.ding.listener.RedisExpiredListener;
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.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer; /**
* @Author lsm
* @Date 2018/10/27 20:56
*/
@Configuration
public class RedisListenerConfig {
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
// container.addMessageListener(new RedisExpiredListener(), new PatternTopic("__keyevent@0__:expired"));
return container;
}
}
  • 3.定义监听器,实现KeyExpirationEventMessageListener接口,查看源码发现,该接口监听所有db的过期事件keyevent@*:expired"
import edu.zut.ding.constants.SystemConstant;
import edu.zut.ding.enums.OrderState;
import edu.zut.ding.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
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"
* @author lsm
*/
@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) {
// 用户做自己的业务处理即可,注意message.toString()可以获取失效的key
String expiredKey = message.toString();
if(expiredKey.startsWith("Order:")){
//如果是Order:开头的key,进行处理
}
}
}
  • 或者打开RedisListenerConfigcontainer.addMessageListener(new RedisExpiredListener(), new PatternTopic("__keyevent@0__:expired")); 注释,再定义监听器,监控__keyevent@0__:expired事件,即db0过期事件。这个地方定义的比较灵活,可以自己定义监控什么事件。
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener; /**
* @author lsm
*/
public class RedisExpiredListener implements MessageListener { /**
* 客户端监听订阅的topic,当有消息的时候,会触发该方法;
* 并不能得到value, 只能得到key。
* 姑且理解为: redis服务在key失效时(或失效后)通知到java服务某个key失效了, 那么在java中不可能得到这个redis-key对应的redis-value。
* * 解决方案:
* 创建copy/shadow key, 例如 set vkey "vergilyn"; 对应copykey: set copykey:vkey "" ex 10;
* 真正的key是"vkey"(业务中使用), 失效触发key是"copykey:vkey"(其value为空字符为了减少内存空间消耗)。
* 当"copykey:vkey"触发失效时, 从"vkey"得到失效时的值, 并在逻辑处理完后"del vkey"
*
* 缺陷:
* 1: 存在多余的key; (copykey/shadowkey)
* 2: 不严谨, 假设copykey在 12:00:00失效, 通知在12:10:00收到, 这间隔的10min内程序修改了key, 得到的并不是 失效时的value.
* (第1点影响不大; 第2点貌似redis本身的Pub/Sub就不是严谨的, 失效后还存在value的修改, 应该在设计/逻辑上杜绝)
* 当"copykey:vkey"触发失效时, 从"vkey"得到失效时的值, 并在逻辑处理完后"del vkey"
*
*/
@Override
public void onMessage(Message message, byte[] bytes) {
byte[] body = message.getBody();// 建议使用: valueSerializer
byte[] channel = message.getChannel();
System.out.print("onMessage >> " );
System.out.println(String.format("channel: %s, body: %s, bytes: %s"
,new String(channel), new String(body), new String(bytes)));
} }

解决方案2

使用spring + quartz定时任务(支持任务信息写入mysql,多节点分布式执行任务),下单成功后,生成一个30分钟后运行的任务,30分钟后检查订单状态,如果未支付,则进行处理

解决方案3

将订单过期时间信息写入mysql,按分钟轮询查询mysql,如果超时则进行处理,效率差!时间精准度底!

解决方案4

使用Java的定时器,不支持高可用,设置定时器的节点挂掉或者重启,任务失效!

Redis Key过期事件的更多相关文章

  1. 如何利用redis key过期事件实现过期提醒

    https://blog.csdn.net/zhu_tianwei/article/details/80169900 redis自2.8.0之后版本提供Keyspace Notifications功能 ...

  2. Node.js 中监听 redis key 过期事件

    It is in fact possible to listen to the “expired” type keyevent notification using a subscribed clie ...

  3. redis中key过期事件

    刚到新公司一个月左右,有个新需求,想做定时任务,比如在用户注册时间的3天后推送用户一条消息. 从刚开始脑子里面闪现的数据库轮询,立马否定掉(浪费资源),再到linux系统的定时任务,但是当用户量过大时 ...

  4. Spring整合redis实现key过期事件监听

    打开redis服务的配置文件   添加notify-keyspace-events Ex  如果是注释了,就取消注释 这个是在以下基础上进行添加的 Spring整合redis:https://www. ...

  5. Spring boot实现监听Redis key失效事件实现和其它方式

    需求: 处理订单过期自动取消,比如下单30分钟未支付自动更改订单状态 用户绑定隐私号码当订单结束取消绑定等 解决方案1: 可以利用redis自带的key自动过期机制,下单时将订单id写入redis,过 ...

  6. SpringBoot实现监听redis key失效事件

    需求: 处理订单过期自动取消,比如下单30分钟未支付自动更改订单状态 解决方案1: 可以利用redis天然的key自动过期机制,下单时将订单id写入redis,过期时间30分钟,30分钟后检查订单状态 ...

  7. 【Redis系列】Spring boot实现监听Redis key失效事件

    talk is cheap, show me the code. 一.开启Redis key过期提醒 方式二:修改配置文件 redis.conf # 默认 notify-keyspace-events ...

  8. Windows 下Redis的部署 及key 过期事件

    window下Redis部署,下载安装完成之后,进入到redis目录: 1.修改配置文件redis.windows.service.conf配置密码 requirepass myRedis (注意在R ...

  9. Redis Key过期通知

    概述 键空间通知使得客户端可以通过订阅频道或模式, 来接收那些以某种方式改动了 Redis 数据集的事件.如Redis数据库中键的过期事件也是通过订阅功能实现.本文主要基于Azure PaaS Red ...

随机推荐

  1. 前端JS编码规范

    对初学者来说应该学习的JavaScript编码规范: 传送门: http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29292475 ...

  2. LintCode之主元素

    题目描述: 分析:由题目可知这个数组不为空且该主元素一定存在,我选用HashMap来存储,HashMap的存储结构是”键—值对“,”键“用来存储数组元素,”值“用来存储这个元素出现的次数,然后循环遍历 ...

  3. C# DataTable删除行Delete与Remove的问题

    DataTable删除行使用Delete后,只是该行被标记为deleted,但是还存在,用Rows.Count来获取行数时,还是删除之前的行数,需要使用datatable.AcceptChanges( ...

  4. C#静态变量总结

    1.初始化 全局static变量的初始化在编译的时候进行,并且只初始化一次 . 函数static变量在函数中有效,第一次进入函数初始化.以后进入函数将沿用上一次的值.  2.生命期 全局static变 ...

  5. 公司C++规范学习

    目录 公司C++规范学习 语法部分 风格/约定 公司C++规范学习 语法部分 class和struct关键字的选择:class表示被封装的用户自定义类型,不公开定义非静态数据成员,struct表示数据 ...

  6. DS静态查找- 顺序-二分-索引

    静态查找 静态表是只执行查找操作,而不执行插入.删除等操作的表. 现在常说的有五大查找方法:顺序查找.分块查找.索引查找.树查找.哈希查找. 后两种之前写过了二叉查找树和哈希表,现在回顾前面三种,它们 ...

  7. Excel 技巧

    <!-- Excel跳转到指定行指定列 --> =HYPERLINK("#"&ADDRESS(要跳转到的行数,要跳转到的列数),"跳转")

  8. 次小生成树(Prim + Kruaskal)

    问题引入: 我们先来回想一下生成树是如何定义的,生成树就是用n - 1条边将图中的所有n个顶点都连通为一个连通分量,这样的边连成子树称为生成树. 最小生成树很明显就是生成树中权值最小的生成树,那么我们 ...

  9. 应该用forEach改变数组的值吗? 原生JS forEach()和map()遍历的异同点

    应该用forEach改变数组的值吗? https://segmentfault.com/q/1010000013170900?utm_source=index-hottest 由于js中的数组是引用类 ...

  10. word2vec 中的数学原理具体解释(二)预备知识

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/peghoty/article/details/37969635   word2vec 是 Googl ...