• swoole实现任务定时自动化调度详解


    开发环境

    环境:lnmp下进行试验

    问题描述

    这几天做银行对帐接口时,踩了一个坑,具体需求大致描述一下。

    银行每天凌晨后,会开始准备昨天的交易流水数据,需要我们这边请求拿到。

    因为他们给的是一个base64加密的zip压缩流,解开以后可以得到txt文件,里面就是我们需要的数据了。

    业务程序写好以后,随手丢了一个定时任务就去睡觉了。

    哪知道第二天上班的时候,检查。发现并没有拿到数据,查询一下日志的时候发现,凌晨服务端请求的时候,银行接口返回了:系统错误信息。

    咨询银行那边后,银行那边相关人员建议我们多请求几次,但是在多次请求中,我发现银行那边是有频率限制的,最后得知,此接口只能半个小时才能请求一次。这就比较尴尬了,因为我不知道银行那边什么时候能返回数据给我。

    于是这个问题怎么解决呢?理想的情况是,服务端请求数据,银行那边没有返回。然后程序等半个小时后,再请求一次,这样一直到银行那边返回正确的数据中止。

    问题分析

    这个功能换作别的语言也许不难,但是通过php实现的话,那就比较麻烦了。通常的话,我们可以搭配linux下的cron来实现,比如我们可以在凌晨到6:00之间做一个定时任务,每半个小时扫描一次php脚本,如果发现银行那边的状态依旧为失败的话,我们就执行一次php脚本去请求数据。直到请求到正确的数据,然后把状态更新为成功。

    这不失为一种方法,但太傻了。比如说银行那边比较正常,凌晨,也就是第一次请求的时候,就已经返回了正确的数据,那么我们的cron脚本还傻傻的每个半个小时执行一次,好蠢!~

    或者我们可以尝试使用linux下的at命令,但感觉还是不够优雅。

    解决问题

    于是决定给laravel扩展一个swoole插件来解决此问题,swoole的定时任务很完美的解决了我们目前的问题。

    首先我们需要把swoole扩展安装好,具体过程略。
    装好以后,我们写一个swoole简易的服务端测试脚本,注意,此脚本是放在app/Console/Commands/下的,笔者是放在了app/Console/Commands/Swoole/swoole.php下,具体代码为

    <?php

    namespace AppConsoleCommandsSwoole;

    use IlluminateConsoleCommand;

    class swoole extends Command
    {
        /**
         * The name and signature of the console command.
         *
         * @var string
         */
        protected $signature = 'swoole {action}';

        /**
         * The console command description.
         *
         * @var string
         */
        protected $description = "Let's use swoole !";

        private $serv;

        /**
         * Create a new command instance.
         *
         * @return void
         */
        public function __construct()
        {
            parent::__construct();
        }

        /**
         * Execute the console command.
         *
         * @return mixed
         */
        public function handle()
        {
            $arg = $this->argument('action');
            switch ($arg) {
                case 'start':
                    $this->info('swoole server started');
                    $this->start();
                    break;
                case 'stop':
                    $this->info('stoped');
                    $this->stop();
                    break;
                case 'restart':
                    $this->info('restarted');
                    break;
            }
        }

        private function start()
        {
            $this->serv = new swoole_server("127.0.0.1", 9501);
            $this->serv->set(array(
                'worker_num' => 8,
                'daemonize' => false,
                'max_request' => 10000,
                'dispatch_mode' => 2,
                'task_worker_num' => 8,
                'task_ipc_mode' => 3,
                'log_file' => storage_path('logs/taskqueue.log'),
            ));

            $this->serv->on('Receive', array($this, 'onReceive'));
            $this->serv->on('Task', array($this, 'onTask'));
            $this->serv->on('Finish', array($this, 'onFinish'));
            $this->serv->start();

        }

        public function onReceive(swoole_server $serv, $fd, $from_id, $data)
        {
            $serv->task($data);
        }

        public function onTask($serv, $task_id, $from_id, $data)
        {
            $timeon = (3) * 1000;
            if ($timeon > 0) {
                $serv->after($timeon, function () {
                    //业务逻辑处
                    exec('php /path/to/root/artisan Test:Command');
                });
            }
            return date('Y-m-d H:i:s') . "第一次执行";
        }

        public function onFinish($serv, $task_id, $data)
        {

            echo "Task finish ";
        }

        private function stop()
        {
            exec('/usr/bin/killall php');
        }

    }

    这是服务端,我们主要用到了after方法,模拟的话,是三秒一执行。实际应该是三十分钟

    然后我们随便写一个客户端连接类

    <?php
    /**
     * Created by PhpStorm.
     * User: nosay
     * Date: 4/13/18
     * Time: 9:27 PM
     */

    namespace AppExtensionphpSwoole;


    class swoole{

        private $data;
        private $client;

        public function __construct($data){
            $this->data = $data;
            $this->client = new swoole_client(SWOOLE_SOCK_TCP);
        }

        public function connect(){


            if( !$this->client->connect("127.0.0.1", 9501 , 1) ) {
                echo "Error";
            }


            $this->client->send($this->data);
        }



    }

    于是我们在银行脚本中就可以去执行了

    <?php

    namespace AppConsoleCommandsTest;

    use AppExtensionphpSwooleswoole;
    use IlluminateConsoleCommand;

    class TestCommand extends Command
    {
        /**
         * The name and signature of the console command.
         *
         * @var string
         */
        protected $signature = 'Test:Command';

        /**
         * The console command description.
         *
         * @var string
         */
        protected $description = 'Command Test';

        /**
         * Create a new command instance.
         *
         * @return void
         */
        public function __construct()
        {
            parent::__construct();
        }

        /**
         * Execute the console command.
         *
         * @return mixed
         */
        public function handle()
        {
            //这里是业务逻辑
            //如果银行那边返回的为false的话,那么我们把他交给swoole的定时脚本
            $status = false;

            if(!$status)
            {
                $swoole = new swoole("hehe");
                $swoole->connect();
            }



        }
    }

  • 相关阅读:
    JQuery OOP 及 OOP思想的简易理解
    windows下编写shell脚本执行错误
    Kafka常用命令
    OffsetDateTime工具类
    windows下安装consul
    磁盘阵列方案
    shell基本语法记录
    学习CGLIB与JDK动态代理的区别
    Spring源码分析-BeanFactoryPostProcessors 应用之 PropertyPlaceholderConfigurer
    局域网内搭建git
  • 原文地址:https://www.cnblogs.com/myJuly/p/12686290.html
Copyright © 2020-2023  润新知