博客
关于我
php如何使用Swoole实现毫秒级定时任务
阅读量:105 次
发布时间:2019-02-26

本文共 5257 字,大约阅读时间需要 17 分钟。

项目开发中,如果有定时任务的业务需求,可以选择使用Linux的crontab工具来处理。但需要注意的是,crontab的最小粒度是分钟级别,对于需要秒级或毫秒级定时任务来说,它就不足以满足需求。幸运的是,Swoole提供了强大的毫秒定时器功能,可以更灵活地满足各种定时任务的需求。

应用场景举例

在实际项目中,我们可能会遇到以下几种定时任务的场景:

  • 场景一:每隔30秒获取一次本机内存使用率

    这种场景常见于系统性能监控中。虽然实时性要求较高,但又能控制好执行频率,适合后台服务器性能监控,可以生成可视化图表。可以是30秒获取一次内存使用率,也可以是10秒,具体取决于业务需求。

  • 场景二:2分钟后执行报表发送任务

    如果需要在特定时间执行一次性任务,crontab虽然可以实现,但比较繁琐。使用Swoole的定时器可以直接定义执行时间,实现更简便。

  • 场景三:每天凌晨2点钟定时请求第三方接口

    这种场景需要考虑接口响应情况。可以设置定时器在5分钟后重试,直到成功或达到最大尝试次数后停止。

  • Swoole毫秒定时器

    Swoole提供了多种异步毫秒定时器函数,支持以毫秒级精度执行定时任务:

    • swoole_timer_tick(int $msec, callable $callback)

      设置一个间隔$msec$毫秒的定时器,每隔$msec$毫秒执行一次$callback$,类似于JavaScript的setInterval()

    • swoole_timer_after(int $after_time_ms, mixed $callback_function)

      在指定的时间$after_time_ms$后执行$callback_function$,类似于JavaScript的setTimeout()

    • swoole_timer_clear(int $timer_id)

      删除指定id的定时器,类似于JavaScript的clearInterval()

    解决方案

    场景一

    可以使用Swoole_Timer_Tick每30秒执行一次,获取本机内存使用率,并将结果存储或发送到指定位置。示例代码如下:

    swoole_timer_tick(30000, function($timer) use ($task_id) {    $memPercent = $this->getMemoryUsage();    echo date('Y-m-d H:i:s') . '当前内存使用率:' . $memPercent . "\n";});

    场景二

    使用Swoole_Timer_After在2分钟后执行特定任务。示例代码如下:

    swoole_timer_after(120000, function() use ($str) {    $this->sendReport();    echo "send report, $str\n";});

    场景三

    可以通过Swoole_Timer_Tick每5分钟执行一次,尝试请求第三方接口。示例代码如下:

    swoole_timer_tick(5 * 60 * 1000, function($timer) use ($url) {    $rs = $this->postUrl($url);    if ($rs) {        // 业务代码...        swoole_timer_clear($timer); // 停止定时器        echo date('Y-m-d H:i:s') . "请求接口任务执行成功\n";    } else {        echo date('Y-m-d H:i:s') . "请求接口失败,5分钟后再次尝试\n";    }});

    示例代码

    文件:src/App/Task.php

    namespace Helloweba\Swoole;use swoole_server;class Task {    protected $serv;    protected $host = '127.0.0.1';    protected $port = 9506;    protected $taskName = 'swooleTask';    protected $pidPath = '/run/swooletask.pid';    protected $options = [        'worker_num' => 4,        'daemonize' => true,        'log_file' => '/data/log/swoole-task.log',        'log_level' => 0,        'dispatch_mode' => 1,        'task_worker_num' => 4,        'task_ipc_mode' => 3,    ];    public function __construct($options = []) {        date_default_timezone_set('PRC');        $this->serv = new swoole_server($this->host, $this->port);        if (!empty($options)) {            $this->options = array_merge($this->options, $options);        }        $this->serv->set($this->options);        $this->serv->on('Start', [$this, 'onStart']);        $this->serv->on('Connect', [$this, 'onConnect']);        $this->serv->on('Receive', [$this, 'onReceive']);        $this->serv->on('Task', [$this, 'onTask']);        $this->serv->on('Finish', [$this, 'onFinish']);        $this->serv->on('Close', [$this, 'onClose']);    }    public function start() {        $this->serv->start();    }    public function onStart($serv) {        cli_set_process_title($this->taskName);        $pid = "{$serv->master_pid}\n{$serv->manager_pid}";        file_put_contents($this->pidPath, $pid);    }    public function onConnect($serv, $fd, $from_id) {        $serv->send($fd, "Hello {$fd}!");    }    public function onReceive(swoole_server $serv, $fd, $from_id, $data) {        echo "Get Message From Client {$fd}:{$data}\n";        $res['result'] = 'success';        $serv->send($fd, json_encode($res));        $serv->task($data);    }    public function onTask(swoole_server $serv, $task_id, $from_id, $data) {        swoole_timer_tick(30000, function($timer) use ($task_id) {            $memPercent = $this->getMemoryUsage();            echo date('Y-m-d H:i:s') . '当前内存使用率:' . $memPercent . "\n";        });    }    public function onFinish(swoole_server $serv, $task_id, $data) {        // ...    }    public function onClose($serv, $fd, $from_id) {        echo "Client {$fd} close connection\n";    }    public function stop() {        $this->serv->stop();    }    private function getMemoryUsage() {        if (false === ($str = @file("/proc/meminfo"))) return false;        $str = implode("", $str);        preg_match_all("/MemTotal\s{0,}\:+\s{0,}([\d\.]+).+?MemFree\s{0,}\:+\s{0,}([\d\.]+).+?Cached\s{0,}\:+\s{0,}([\d\.]+).+?SwapTotal\s{0,}\:+\s{0,}([\d\.]+).+?SwapFree\s{0,}\:+\s{0,}([\d\.]+)/s", $str, $buf);        $memTotal = round($buf[1][0]/1024, 2);        $memFree = round($buf[2][0]/1024, 2);        $memUsed = $memTotal - $memFree;        $memPercent = (floatval($memTotal) != 0) ? round($memUsed/$memTotal*100, 2) : 0;        return $memPercent;    }}

    文件:src/App/TaskServer.php

    false,];$ser = new Task($opt);$ser->start();

    文件:src/App/TaskClient.php

    client = new swoole_client(SWOOLE_SOCK_TCP); } public function connect() { if (!$this->client->connect("127.0.0.1", 9506, 1)) { echo "Error: {$this->client->errMsg}[{$this->client->errCode}]\n"; } fwrite(STDOUT, "请输入消息 Please input msg:"); $msg = trim(fgets(STDIN)); $this->client->send($msg); $message = $this->client->recv(); echo "Get Message From Server:{$message}\n"; }}$client = new Client();$client->connect();

    验证效果

  • 启动服务端

    在命令行执行:

    php taskServer.php
  • 客户端输入

    在另一个命令行窗口执行:

    php taskClient.php

    输入消息:Please input msg: hello

  • 服务端返回

    应用将输出类似以下内容:

    Get Message From Server: {"result":"success"}
  • 定时任务输出

    每隔30秒,服务端将输出当前内存使用率的信息。

  • 通过以上步骤,可以验证Swoole毫秒定时器的功能是否正常工作。

    转载地址:http://pgdk.baihongyu.com/

    你可能感兴趣的文章
    Netty工作笔记0006---NIO的Buffer说明
    查看>>
    Netty工作笔记0007---NIO的三大核心组件关系
    查看>>
    Netty工作笔记0011---Channel应用案例2
    查看>>
    Netty工作笔记0013---Channel应用案例4Copy图片
    查看>>
    Netty工作笔记0014---Buffer类型化和只读
    查看>>
    Netty工作笔记0020---Selectionkey在NIO体系
    查看>>
    Vue踩坑笔记 - 关于vue静态资源引入的问题
    查看>>
    Netty工作笔记0025---SocketChannel API
    查看>>
    Netty工作笔记0027---NIO 网络编程应用--群聊系统2--服务器编写2
    查看>>
    Netty工作笔记0050---Netty核心模块1
    查看>>
    Netty工作笔记0057---Netty群聊系统服务端
    查看>>
    Netty工作笔记0060---Tcp长连接和短连接_Http长连接和短连接_UDP长连接和短连接
    查看>>
    Netty工作笔记0063---WebSocket长连接开发2
    查看>>
    Netty工作笔记0070---Protobuf使用案例Codec使用
    查看>>
    Netty工作笔记0077---handler链调用机制实例4
    查看>>
    Netty工作笔记0084---通过自定义协议解决粘包拆包问题2
    查看>>
    Netty工作笔记0085---TCP粘包拆包内容梳理
    查看>>
    Netty常用组件一
    查看>>
    Netty常见组件二
    查看>>
    netty底层源码探究:启动流程;EventLoop中的selector、线程、任务队列;监听处理accept、read事件流程;
    查看>>