注意:redis过期通知阻塞模式 需要用linux守护进程运行
当我们的平台上的用户下了订单,但是没有付款时,我们一般会给这个订单保存 两个小时的时间 ,两小时过后,就要对这个订单进行做废处理,把订单上的商品的库存归还
有两种方法来实现,
一.服务器的定时任务 crontab
这种方式的缺点,就是时间不是那么准确,如果要两个小时定单过期,我的方法是让定时任务 一个小时执行一次,对全部未付款的订单进行查找,并设置过期,用foreach循环来返还库存
二.就是使用redis的发布订阅机制
这个方法可以做到时间准确, 而且是一条一条的数据修改,这里主要说第二种方法
它主要是用了redis的发布订阅机制,redis的notify-keyspace-events 会自动发布一个频道 频道的名称有固定的格式,这里我们监听 键的失效事件 的订阅频道名是 __keyevent@0__:expired 订阅了这个频道,当redis的 第0号数据库中有键名失效时,就会收到一个推送,我们就利用这个特点来完成功能
首先,安装redis,
配置:这果在 redis.conf中 设置监听,键的失效事件 notify-keyspace-events Ex
配置好后,重启redis的服务端
PHP代码,设置一个订单30分钟过期<?php
$redis = new \Redis();
$redis->connect('127.0.0.1',6379);
$orderNum = '5edde4c84fff0'; //订单编号,值必须唯一
Db::name('order')->where(['uuid' => $orderNum ])->update(['expire_time' => time()+30*60]);
$redis->setex($uuid,30*60,43); //设置key value 和过期时间
return 'success';
?>
php-cli模式执行php文件:
namespace app\common\command\redis;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Db;
class Subscribe extends Command
{
protected function configure()
{
parent::configure(); // TODO: Change the autogenerated stub
$this->setName('redis-subscribe'); //命令名称 php think redis-subscribe
}
public function execute(Input $input,Output $output) {
$redis = new \Redis();
$redis->connect('127.0.0.1',6379);
$redis->setOption(\Redis::OPT_READ_TIMEOUT,-1); //设置当前文件redis设置永不过期
$redis->psubscribe(array('__keyevent@0__:expired'), 'app\common\command\redis\Subscribe::keyCallback');//回调必须写绝对路径 要不然 会报错
}
public static function keyCallback($redis, $pattern, $channel, $message) {
file_put_contents('subscribe_msg.log',$message."\r\n",FILE_APPEND);
//随便写个 log 记录 不要复制 要不然会在项目的 根目录出现 记得写路径
//在这里写逻辑啊 写数据库操作
$where = ['uuid' => $message];
Db::name('order')->where($where)->update(['status' => 2]);
echo '已修改订单编号:'.$message;
}
}
?>
这里我用tp框架的command自定义命令来跑
nohup + & 实现redis命令永久后台运行:
nohub:使程序永久执行下去,SSH断开连接都不会影响他的运行
&:后台运行,程序执行完挂起的时候命令也会退出
nohup command &
这样就能使命令永久的在后台执行