zoukankan      html  css  js  c++  java
  • PHP【Laravel】delayer基于redis的实现订单超时改变状态

    实现这个功能前你需要知道以下,不然可能会比较吃力:
    1.服务器的计划任务,shell脚本,或者你有宝塔自带的计划任务会方便很多。
    2.有所了解Redis。
    3.会写PHP业务逻辑。

    好了进入在正题,这里使用一个库delayer。
    它是 基于 Redis 的延迟队列中间件,采用 Golang 开发,支持 PHP、Golang 等多种语言客户端。
    因此在你的项目中,你需要为PHP装上redis扩展。注意不是Laravel的predis。就是你phpinfo()能看到有redis这个扩展。

    之后为你的项目加上delayer
    你的服务端需要:(就是在服务器中装上delayer,然后在服务器上启动)
    https://github.com/mix-basic/delayer
    你的客户端需要:(就是在你的项目中装上delayer类库,让你的代码能到delayer的四个基本操作)
    https://github.com/mix-basic/delayer-client-php

    然后总结一下client这边四个方法:
    1.push(放入任务)

    //配置好连接redis的信息
    $client = new DelayerClient(config('database.redis.default'));

    //data为你要存入的数据
    $data = [
    'orderID' => '20181017125789566488854744',
    'action' => 'close',
    ];

    //发送信息
    $message = new Message([
    // 任务ID,必须全局唯一
    'id' => md5(uniqid(mt_rand(), true)),
    // 主题,取出任务时需使用
    'topic' => 'close_order',
    // 必须转换为string类型
    'body' => json_encode($data),
    ]);

    /
    * push($message, 10, 30)
    * 第2个参数为延迟时间,第3个参数为延迟到期后如果任务没有被消费的最大生存时间(秒)
    * 延迟时间:参数2设置时间到了才能pop出任务,否则pop返回false;
    * 最大生存时间:参数2的时间到之后。倒计时参数3的时间。超时再pop返回false。
    *
    /
    $ret = $client->push($message, 10, 30);
    var_dump($ret);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    2.pop(取到到期任务)

    /
    * 配置好连接redis的信息
    * 要与Delayer服务器端配置的redis信息相同
    /
    $client = new DelayerClient(config('database.redis.default'));
    //根据之前的topic值取出最先放入且没有消亡的任务
    $message = $client->pop('close_order');
    1
    2
    3
    4
    5
    6
    7
    存在则返回如下:

    任务被pop获取一次后就消亡了。再pop的话返回false或者是下一个到期任务。

    3.bPop(阻塞取出)

    $message = $client->bPop('close_order', 1);//这里相当于1秒后执行pop
    1
    4.remove(移除任务)

    // push时定义的任务ID
    $id = '***';
    $ret = $client->remove($id);
    var_dump($ret);
    1
    2
    3
    4
    移出一个未到期任务。成功true,到期或消亡返回false。

    ----------------------------------------------以上是delayer的操作说明---------------------------------------------------

    下面说明业务逻辑思路:

    1.首先在服务器执行计划任务(定时任务)。这里我以宝塔作为例子。如下图:


    shell脚本内容:

    step=10
    for (( i = 0; i < 60; i=(i+step) )); do
    /www/server/php/73/bin/php /www/wwwroot/phal_admin/autoTask/deal_turn_format_list_result.php
    sleep $step
    done
    exit 0
    1
    2
    3
    4
    5
    6
    脚本说明
    step =10 //代表10秒运行一次。
    do 路径一(服务器如linux中php的可执行文件) 路径二(PHP的delayer取消订单的业务逻辑)
    路径二的示例代码:

    <?php
    use AppModelsShopOrder; //你的订单模型
    use DelayerClient; //delayer

    require '/home/vagrant/code/public/init.php'; //必须项目入口文件,我这里是laravel

    $client = new DelayerClient(config('database.redis.default')); //配置参数,不明白说明你没好好看

    for ($x=0; $x<10; $x++) {
    $result = $client->pop('close_order');
    if(!$result ){ //服务器一直执行pop,否则break
    break;
    }
    ShopOrder::cancelOrder($result->id); //如果pop到了超时订单就取消订单状态。这里自定义你的业务。
    echo('订单超时取消!');
    }

    ?>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    完成了以上恭喜你基本完成功能的大部分,此时你的服务器已经会自动对你的超时订单进行处理。

    2.项目中的业务逻辑
    现在服务器已经会对你的超时订单进行处理。
    你只需要在未付款订单生成的时候push任务,用户付款的时候remove任务。
    下面上伪代码:

    未付款订单生成时(push入任务)


    $data = [
    //自定义内容
    ];

    //发送信息
    $message = new Message([
    // 任务ID,必须全局唯一,这里可以放订单id(业务中remove需要)
    'id' => 32,
    // 主题,取出任务时需使用(服务器pop需要)
    'topic' => 'close_order',
    // 必须转换为string类型
    'body' => json_encode($data),
    ]);

    //放入任务,例如你的需求是30分钟自动取消订单。那么就是1800秒。后面30是消亡时间,自定义。
    push($message, 1800, 30)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    用户付款之后(从任务中remove)

    $client->remove($id); //message里你放入的id,如:上面的32
    1
    这样一个简单订单超时处理系统算是完成了。

    如有不足或错误欢迎指出~
    ---------------------

  • 相关阅读:
    循环语句的基本使用
    创建一个可拖动的dom元素。
    JavaScript中的callee,caller,call,apply的使用
    两个数组去重的方法。
    利用setTimeout建立能捕捉鼠标多次点击和鼠标长按的事件处理程序。
    document.getElementByClassName()的使用和兼容老浏览器。
    jQuery .data()方法的运用。
    javascript对象的深拷贝。
    未来、
    linux上机作业
  • 原文地址:https://www.cnblogs.com/ly570/p/11173777.html
Copyright © 2011-2022 走看看