原文:https://www.jianshu.com/p/92b331355b8d
订单超时未支付自动关闭实现的几种方式:
1,Quartz 任务调度框架,更适合周期性的执行任务,对于订单超时未支付,只能采用5分钟一轮询数据库的形式实现
2,Timer java原生定时工具,可少量使用,当数据量大时,性能不好控制
3,Quartz + Timer 周期轮询(5分钟)数据库,查询出5分钟之内将要超时的订单,然后多线程创建timer完成订单的定时,这种实现方式比较复杂,但是可以在性能和功能方面,是可以实现的
4,reids 键空间通知
以下将介绍此种方式
(其实用的是Spring-data的方式,不过网上能找到的就只有这种了好像)
redis 键空间通知实现
过期事件通过Redis的订阅与发布功能(pub/sub)来进行分发。
redis服务端配置
超时的监听,并不需要自己发布,只有修改配置文件redis.conf中的:
notify-keyspace-events Ex,默认为notify-keyspace-events “”
E:启用keyevent键事件通知,客户端可以使用keyevent@为前缀的格式使用订阅功能;
x:启用对过期事件的监控;
Spring-mybatis.xml中的配置
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}"
p:password="${redis.pass}" p:pool-config-ref="jedisPoolConfig" />
<bean id="messageListener"
class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="com.wjh.listener.MyRedisKeyExpiredMessageDelegate" />
</constructor-arg>
</bean>
<bean id="redisContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="messageListeners">
<map>
<entry key-ref="messageListener">
<list>
<bean class="org.springframework.data.redis.listener.ChannelTopic">
<constructor-arg value="__keyevent@0__:expired" />
</bean>
</list>
</entry>
</map>
</property>
</bean>
package com.wjh.listener;
import java.io.Serializable;
import java.util.Map;
/**
* Author: 17976
* Date: 2019/2/23 21:41
* Description:
*/
public interface MyMessageDelegate {
void handleMessage(String message);
void handleMessage(Map message);
void handleMessage(byte[] message);
void handleMessage(Serializable message);
// pass the channel/pattern as well
void handleMessage(Serializable message, String channel);
}
package com.wjh.listener;
import java.io.Serializable;
import java.util.Map;
/**
* Author: 17976
* Date: 2019/2/23 21:42
* Description:
*/
public class MyRedisKeyExpiredMessageDelegate implements MyMessageDelegate {
@Override
public void handleMessage(String message) {
}
@Override
public void handleMessage(Map message) {
}
@Override
public void handleMessage(byte[] message) {
}
@Override
public void handleMessage(Serializable message) {
}
@Override
public void handleMessage(Serializable message, String channel) {
}
}
package com.wjh.listener;
import redis.clients.jedis.JedisPubSub;
/**
* Author: 17976
* Date: 2019/2/23 21:34
* Description: 监听redis key是否过期
*/
public class KeyExpiredListener extends JedisPubSub {
@Override
public void onPSubscribe(String pattern, int subscribedChannels) {
// super.onPSubscribe(pattern, subscribedChannels);
System.out.println("onPSubscribe " + pattern + " " + subscribedChannels);
}
@Override
public void onPMessage(String pattern, String channel, String message) {
System.out.println("onPMessage pattern -->" + pattern + "-->channel: " + channel + "message--> " + message);
}
}
package com.wjh.listener;
import com.wjh.dao.RedisDao;
import com.wjh.dao.impl.RedisDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.List;
/**
* Author: 17976
* Date: 2019/2/23 22:29
* Description: redis订阅程序
*/
public class Subscriber {
public static void main(String[] args) {
JedisPool pool = new JedisPool(new JedisPoolConfig(), "116.62.140.147",6379);
Jedis jedis = pool.getResource();
jedis.auth("flower");
config(jedis);
//只订阅patten匹配在超时事件
jedis.psubscribe(new KeyExpiredListener(), "__key*@0__:expired");
}
private static void config(Jedis jedis) {
String parameter = "notify-keyspace-events";
List<String> notify = jedis.configGet(parameter);
if (notify.get(1).equals("")) {
jedis.configSet(parameter, "Ex"); //过期事件
}
}
}
package com.wjh.listener;
/**
* Author: 17976
* Date: 2019/2/23 22:26
* Description: 测试redis key超时
*/
import com.wjh.dao.RedisDao;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisListenerTest {
public static void main(String[] args){
JedisPool pool = new JedisPool(new JedisPoolConfig(), "116.62.140.147",6379);
Jedis jedis = pool.getResource();
jedis.auth("flower");
jedis.select(0);
jedis.set("notify", "你还在吗");
jedis.expire("notify", 5);
jedis.select(1);
jedis.set("test","test");
jedis.expire("test", 10);
}
}
先运行订阅程序Subscriber
onPSubscribe __key*@0__:expired 1
再运行测试程序RedisListenerTest,在0库和1库分别放入两个String值,并设置过期时间
这时Subscriber应该输出
onPMessage pattern -->__key*@0__:expired-->channel: __keyevent@0__:expiredmessage--> notify
但是我的没有输出啊,KeyExpiredListener中的onPMessage没有回调成功。
解决办法:
https://blog.csdn.net/liuhaiabc/article/details/80825426
修改Subscriber判断redis配置文件中是否设置了notify-keyspace-events EX,如果设置没有生效就手动设置。
(不知道是不是应该修改完之后把redis重启一下呢,这次设置了之后我把这个conf方法给去掉了,这下又可以了。)
public class Subscriber {
public static void main(String[] args) {
JedisPool pool = new JedisPool(new JedisPoolConfig(), "116.62.140.147",6379);
Jedis jedis = pool.getResource();
jedis.auth("flower");
config(jedis);
//只订阅patten匹配在超时事件
jedis.psubscribe(new KeyExpiredListener(), "__key*@0__:expired");
}
private static void config(Jedis jedis) {
String parameter = "notify-keyspace-events";
List<String> notify = jedis.configGet(parameter);
if (notify.get(1).equals("")) {
jedis.configSet(parameter, "Ex"); //过期事件
}
}
哈哈,myorder是我三个小时之前设置的,刚好这会儿过期了。