最新公告
  • 欢迎您访问爱上源码网,分享精品整站源码,网站模板,游戏源码,APP小程序源码以及视频教程免费下载;服务永无止境!立即加入我们
  • PHP 简单实现延时操作

    爱上源码网文章PHP 简单实现延时操作的内容插图

    爱上源码网文章PHP 简单实现延时操作的内容插图1

    场景

    在业务中有时会碰到延迟操作,如下单后半小时未支付则取消订单、下单后十五分钟未支付则发短信提醒等等。那这样的需求如何去实现呢。

    相关学习推荐:PHP编程从入门到精通

    实现方式

    • 第一个简单的方式就是用一个后台进程死循环去查订单,根据下单时间去做不同的操作
    • 第二种就是使用消息队列的定时消息,下单之后发送定时消息,不同的定时队列去处理不同的逻辑
    • 第三种可以使用框架提供的一些既有功能去做

    实现代码

    我们以订单创建15分钟后未支付,给用户发送邮件为场景进行学习

    准备工作:

    1. 简单的订单表:order
    2. 各种需要的composer包
    3. rabbitMq本地服务
    4. 开通阿里云RocketMq服务

    第一种

    • 代码逻辑很简单就直接死循环就行了
    • 启动这个脚本进程,可以用supervisor配置
    • 部分代码
    //创建订单的逻辑/**
     * 随机创建订单
     */$order = [
        'order_number' => mt_rand(100,10000).date("YmdHis"),
        'user_id' => mt_rand(1, 100),
        'order_amount' => mt_rand(100, 1000),];
        /**@var $manager Illuminate\Database\Capsule\Manager **/
        $conn = $manager;$insertResult = $conn::table("order")
        ->insert($order);print_r($insertResult);

    延迟处理逻辑

    while(true) {
        // 未支付订单列表
        $orderList = $conn::table("order")
            ->where("created_time",  '<=', date("Y-m-d H:i:s", strtotime("-15 minutes")))
            ->where('sended_need_pay_notify', '=', 2)
            ->where('status', '=', 1)
            ->select(['user_id', 'id'])
            ->orderBy("id", 'asc')
            ->get();
        $orderList = json_decode(json_encode($orderList), true);
        foreach ($orderList as $orderInfo) {
            sendEmail($orderInfo['user_id']);
            $conn::table('order')
                ->where('id', '=', $orderInfo['id'])
                ->update(['sended_need_pay_notify' => 1]);
            logs("update-success-orderId-". $orderInfo['id']."-userId-".$orderInfo['user_id']);
        }
    
        sleep(10);}

    执行处理脚本

    gaoz@nobodyMBP delay_mq_demo % php first_while_handler.php
    send email to 73 success ...
    2020-06-24 11:37:36:update-success-orderId-3-userId-73

    这种方式吧实现简单,但是不优雅,同时大批量订单产生也会遇到问题。

    第二种

    • 比如使用阿里云的MQ服务,目前rocketMq与rabbitMq版本支持延迟消息,但是rabbit的延时消息收费太高了
    • 这里先使用rocketMq的延迟消息去实现
    • 需要开通阿里云的服务
    // 创建订单的逻辑try
            {
    
                /**
                 * 随机创建订单
                 */
                $order = [
                    'order_number' => mt_rand(100,10000).date("YmdHis"),
                    'user_id' => mt_rand(1, 100),
                    'order_amount' => mt_rand(100, 1000),
                ];
    
                /**@var $manager Illuminate\Database\Capsule\Manager **/
                $conn = $manager;
    
                $insertId = $conn::table("order")
                    ->insertGetId($order);
    
                $body = json_encode(['order_id' => $insertId, 'created_time' => date("Y-m-d H:i:s")]);
                $publishMessage = new TopicMessage(
                    $body            );
                // 设置消息KEY
                $publishMessage->setMessageKey("MessageKey");
    
                // 定时消息, 定时时间为3分钟后
                $publishMessage->setStartDeliverTime(time() * 1000 + 3 * 60 * 1000);
    
                $result = $this->producer->publishMessage($publishMessage);
    
                print "Send mq message success. msgId is:" . $result->getMessageId() . ", bodyMD5 is:" . $result
                -
                >getMessageBodyMD5() . "\n";
            } catch (\Exception $e) {
                print_r($e->getMessage() . "\n");
            }

    消费逻辑 同样是在消费者中处理

    foreach ($messages as $message) {
                    $receiptHandles[] = $message->getReceiptHandle();
    
                    $messageBody = $message->getMessageBody();
    
                    $orderInfo = json_decode($messageBody, true);
                    if (!empty($orderInfo['order_id'])) {
                        $orderId = $orderInfo['order_id'];
    
                        /**@var $manager Illuminate\Database\Capsule\Manager * */
                        $conn = $manager;
                        $orderInfo = $conn::table("order")
                            ->select(['id', 'user_id'])
                            ->where('id', '=', $orderId)
                            ->where('status', '=', 1)
                            ->first();
                        if (!empty($orderInfo)) {
                            $orderInfo = json_decode(json_encode($orderInfo), true);
                            sendEmail($orderInfo['user_id']);
                            $conn::table('order')
                                ->where('id', '=', $orderInfo['id'])
                                ->update(['sended_need_pay_notify' => 1]);
                            logs("update-success-orderId-" . $orderInfo['id'] . 
                            "-userId-" . $orderInfo['user_id']);
                        }
                    }
                }

    启动生产一条消息

    gaoz@nobodyMBP delay_mq_demo % php rocket_mq_handler_producer.php 
    Send mq message success. msgId is:76CF2135696C3D4EAC698A9FA1E1879D, bodyMD5 
    is:63448B50AA7B8AF47B07AA7CE807E3D3
    gaoz@nobodyMBP delay_mq_demo %

    启动消费者慢慢等待

    gaoz@nobodyMBP delay_mq_demo % php rocket_mq_handler_consumer.php 
    No message, contine long polling!RequestId:5EF752583441411C74869BA9
    No message, contine long polling!RequestId:5EF7525B3441411C74869FE2
    No message, contine long polling!RequestId:5EF7525E3441411C7486A42C
    No message, contine long polling!RequestId:5EF752613441411C7486A7D9
    consume finish, messages:send email to 95 success ...2020-06-27 12:08:05:update-success-orderId-8-userId-95
     Array(
        [0] => 76CF2135696C3D4EAC698A9FA1E1879D-MCAxNTkzMjY2NzkxNDM5IDMwMDAwMCAzIDAgYmpzaGFyZTUtMDggNSAw)
        ack

    这种方式有现有的服务可以使用,减少开发时间

    第三种 使用rabbitMq去实现

    • 查阅文档没有找到rabbitMq支持延迟队列的原生功能,但是可以通过消息的ttl+死信队列实现
    • 私信队列就是用来存放没有被消费或者消费失败等消息的队列
    • 当设置消息的有效期内没有被消费消息就会被转发到死信队列
    • 通过设置消息的有效期实现延时功能
    // 生产者$exchange = 'order15min_notify_exchange';
    $queue = 'order15minx_notify_queue';$dlxExchange = "dlx_order15min_exchange";
    $dlxQueue = "dlx_order15min_queue";
    $connection = new AMQPStreamConnection(getenv('RABBIT_HOST'), getenv('RABBIT_PORT'), getenv("RABBIT_USER"), getenv("RABBIT_PASS"), getenv("RABBIT_VHOST"));
    $channel = $connection->channel();$channel->exchange_declare($exchange, AMQPExchangeType::DIRECT, false, true, false);
    $channel->exchange_declare($dlxExchange, AMQPExchangeType::DIRECT, false, true, false);// 设置队列的过期时间// 正常队列$table = new \PhpAmqpLib\Wire\AMQPTable();// 消息有效期$table->set('x-message-ttl', 3*60*1000);$table->set("x-dead-letter-exchange", $dlxExchange);$channel->queue_declare($queue, false, true, false, false, false, $table);$channel->queue_bind($queue, $exchange);// 死信队列$channel->queue_declare($dlxQueue, false, true, false, false, false);$channel->queue_bind($dlxQueue, $dlxExchange);/**
     * 随机创建订单
     */$order = [
        'order_number' => mt_rand(100,10000).date("YmdHis"),
        'user_id' => mt_rand(1, 100),
        'order_amount' => mt_rand(100, 1000),];/**@var $manager Illuminate\Database\Capsule\Manager **/$conn = $manager;$insertId = $conn::table("order")
        ->insertGetId($order);$messageBody = json_encode(['order_id' => $insertId, 'created_time' => date("Y-m-d H:i:s")]);
        $message = new AMQPMessage($messageBody, array('content_type' => 'text/plain', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT));
        $channel->basic_publish($message, $exchange);

    消费者

    $dlxExchange = "dlx_order15min_exchange";$dlxQueue = "dlx_order15min_queue";
    $connection = new AMQPStreamConnection(getenv('RABBIT_HOST'), getenv('RABBIT_PORT'), getenv("RABBIT_USER"), getenv("RABBIT_PASS"), getenv("RABBIT_VHOST"));
    $channel = $connection->channel();
    $channel->queue_declare($dlxQueue, false, true, false, false);$channel->exchange_declare($dlxExchange, AMQPExchangeType::DIRECT, false, true, false);
    $channel->queue_bind($dlxQueue, $dlxExchange);/**
     * @param \PhpAmqpLib\Message\AMQPMessage $message
     */function process_message($message){
        echo "\n--------\n";
        echo $message->body;
        echo "\n--------\n";
    
        $orderInfo = json_decode($message->body, true);
        if (!empty($orderInfo['order_id'])) {
            $orderId = $orderInfo['order_id'];
    
            /**@var $conn Illuminate\Database\Capsule\Manager * */
            $conn = getdb();
            $orderInfo = $conn::table("order")
                ->select(['id', 'user_id'])
                ->where('id', '=', $orderId)
                ->where('status', '=', 1)
                ->first();
            if (!empty($orderInfo)) {
                $orderInfo = json_decode(json_encode($orderInfo), true);
                sendEmail($orderInfo['user_id']);
                $conn::table('order')
                    ->where('id', '=', $orderInfo['id'])
                    ->update(['sended_need_pay_notify' => 1]);
                logs("update-success-orderId-" . $orderInfo['id'] . "-userId-" . $orderInfo['user_id']);
            }
    
        }
        $message->delivery_info['channel']->basic_ack(
            $message->delivery_info['delivery_tag']);}$channel->basic_consume($dlxQueue, $consumerTag, false, false, false, false, 'process_message');

    启动消费者

    gaoz@nobodyMBP delay_mq_demo % php rabbit_mq_handler_consumer.php
    --------
    {"order_id":7,"created_time":"2020-06-27 11:50:08"}
    --------
    send email to 2 success ...
    2020-06-27 11:56:55:update-success-orderId-7-userId-2

    分别启动消费者、生产者就可以了,这里面消息的流转可以看到

    爱上源码网文章PHP 简单实现延时操作的内容插图2

    爱上源码网文章PHP 简单实现延时操作的内容插图3

    消息先进入到正常队列,过期后进入了死信队列而被消费

    第四种

    • 使用laravel自带的Queue去实现
    • 这里没有整理详细代码,后面更新出来
    • 可以查看官方文档 队列《Laravel 5.7 中文文档》

    代码示例:github.com/nobody05/delay_mq_demo

    以上就是PHP 简单实现延时操作的详细内容,更多请关注爱上源码网其它相关文章!

  • 微信
  • 分享
  • 相关标签:PHP 延时操作
  • 本文转载于:learnku,如有侵犯,请联系916990011@qq.com删除
    • 上一篇:详细解读PHP中return用法(附代码)
    • 下一篇:学习PHP死循环写法和作用

    相关文章

    相关视频

    • PHP实现订单延时处理的方法实例
    • PHP7之Reids键空间通知配合TP5 实现分布…
    • php延时几秒后搜索
    • java怎么实现延时
    • PHP 简单实现延时操作
    • ThinkPHP6.0 杂项
    • PHP变量

    本文有爱上源码下载完入驻作者发布,如果对您版权造成侵害,可以联系本站站长管理进行维权删除,本站收到维权24小时内进行处理,谢谢您关注23ym.cn!
    本站分享大量程序员技术文章以及对编程开发的初级入门教程,包括图文讲解笔记和高清视频下载~

    重要声明:
    1.本站视频教程,软件及网站源码版权均属于原作者所有,您必须在下载后的24个小时之内,从您的电脑中删除!非法商业用途,后果自负!
    2.本站不保证所提供下载资源的安全性和完整性,仅供下载学习之用!如链接失效或资源含外站广告,请联系客服处理!给予奖励!
    3.本站所有资源来源于用户上传和网络,因此不包含技术服务请大家谅解!本站提供有偿服务!如有侵权请联系在线客服!
    4.如您手中有优质资源或教程,可以自助投稿发布,成功分享后有奖励和额外收入!
    5.如您需要正版微擎模块可联系本站客服,我们有价值30w+商业微擎应用出售微擎坑位和招收代理!
    6.400电话/软著/ICP,EDI许可证/商标特价办理中!
    爱上源码下载网 » PHP 简单实现延时操作

    常见问题FAQ

    从网站下载的源码都有安装教程么?不会安装怎么办?
    本站发布的网站源码和模板资源大部分在压缩包内都有教程,如您不会安装可以联系本站在线技术进行付费安装。
    爱上源码的所有源码都是亲测能正常运行的么?
    本站目前拥有资源10w+,包含整站源码,网站模板,游戏源码,小程序源码,视频教程,破解软件等,每天也在测试更新;因时间和精力有限我们无法对资源进行一一测试,只能保证所分享资源内容无误,希望理解。
    我手中的优质资源可以在你这换钱或者VIP么?
    爱上源码支持投稿,欢迎发布您手中的优质资源进行售卖;本站VIP支持免费获取,目前邀请10人注册爱上源码即可免费获取VIP。
    爱上源码除了资源分享还有其他业务没?
    【价值30W+微擎模块出售正版商业微擎坑位及招收代理,详情咨询本站客服!】我们团队目前运营并推广几套商业化saas智能小程序系统能满足大部分小程序开发需求,并由SaaS和独立部署版商城小程序系统;另外销售400电话,各种ICP/EDI资质证书办理,软著和商标注册服务等。

    发表评论

    • 27会员总数(位)
    • 35644资源总数(个)
    • 0本周发布(个)
    • 0 今日发布(个)
    • 479稳定运行(天)

    提供最优质的资源集合

    开通VIP 源码下载