PHP+Redis实现延时任务

需求:如果某个订单在某时间内还没有支付,就自动取消。

之前在公司一直都是使用Linux的定时任务,全表扫描未支付的订单,然后判断订单是否到期,如果到期了则改变订单的状态,这样一来因为使用了全表扫描,当业务量大的时候,效率会很低。

Redis应用:redis的keyspace notifications 会在key失效后发送一个事件,监听此事件的的客户端就可以收到通知(需要注意此功能是在redis 2.8版本以后推出的,因此你服务器上的reids最少要是2.8版本以上)

------------------------------开始------------------------------

先修改Redis配置(Linux系统)

打开Redis配置文件 redis.conf

把默认的

notify-keyspace-events ""

改成

notify-keyspace-events "Ex"

然后重启Redis

------------------------------代码开始------------------------------

代码我简略写了,需要什么业务逻辑根据需要写

数据表结构:

CREATE TABLE `shop_order` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `orderid` varchar(20) NOT NULL DEFAULT '' COMMENT '订单id',
 `pay` tinyint(4) NOT NULL DEFAULT '0' COMMENT '支付状态 0:未支付 1:已支付',
 `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '订单状态 0:待支付 1:订单自动关闭',
 `createtime` int(11) NOT NULL COMMENT '订单创建时间',
 PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=27 DEFAULT CHARSET=utf8

PHP文件名 index.php代码

try {
    $dsn         = 'mysql:host=127.0.0.1;dbname=shop;';
    $userName    = 'root';
    $passWord    = 'root';
    $pdo         = new PDO($dsn, $userName, $passWord);
    $orderid     = uniqid();
    $createTime  = time();
    /*把订单信息写入MySql 需要其他信息和逻辑自己添加即可*/
    $sql = "INSERT shop_order (orderid,createtime) VALUES ('$orderid','$createTime')";
    $pdo->exec($sql);
    /*把订单信息写入Redis 需要其他信息和逻辑自己添加即可*/
    $redis = new Redis();
    $redis->connect('127.0.0.1','6379');
    //订单在10s后回调
    $redis->setex($orderid,10,$orderid);
} catch (PDOException $e) {
    die ($e->getMessage());
}

index.php的功能就是简单地把订单号写入到数据库和Redis,并且设置Redis数据的过期时间(即订单过期的时间)

PHP文件名 sub.php代码

keyevent@__:expired 这个格式是固定的,db代表的是数据库的编号,订阅开启之后这个库的所有key过期时间都会被推送过来

ini_set('default_socket_timeout', -1);
$redis = new Redis();
$res = $redis->connect('127.0.0.1', '6379');
$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);
$redis->subscribe(array('__keyevent@0__:expired'), @function ($redis, $pattern, $channel, $msg){
    $orderid = $channel;
    echo '订单号为'.$channel.'已经过期,正在为你自动取消订单.....\r\n';
    $userName    = 'root';
    $passWord    = 'root';
    $dsn         = 'mysql:host=127.0.0.1;dbname=shop;';
    $pdo         = new PDO($dsn, $userName, $passWord);
    //这个订单还没有支付并且这个订单已经到期了
    $sql1        = "SELECT * FROM shop_order WHERE status = 0 and pay = 0 and orderid = '$orderid'";
    $incRes      = $pdo->query($sql1);
    if($incRes && $incRes->rowCount()){
    //把这个订单的状态设置为自动关闭状态
        $sql2   =  "update shop_order set `status` = 1 where orderid = '$orderid'";
        $upRes  =  $pdo->exec($sql2);
        if($upRes){
            echo "订单号为:{$orderid}已取消成功....\r\n";
        }
    }
});

sub.php的功能就是key失效后发送一个事件,在这里收到通知,拿到订单号,设置订单的状态

测试:

先使用脚本运行sub.php

然后用浏览器运行index.php(过了几秒 Redis的某个key过期之后,我的控制台就会出现......)

订单号都拿到了, ...还有什么做不到的呢?

个人博客:https://www.521bug.cn

你可能感兴趣的:(PHP,Redis)