swoole 第8章 定时器

2021-06-09 鲁鲁槟 收藏

一、前言

说起定时器,大家都不陌生。我最早接触定时器的概念,是javascript的setInterval和setTimeout这两个函数,前者会持续执行,后者仅会执行一次。

在后端开发中,一些涉及到定时器相关的需求,比如数据库备份,排行榜数据更新等,通常我们可以借助linux的crontab工具实现。但是对于一些想精确到秒级别或者想暂停定时器的需求,就相对麻烦一些了。

直到swoole的诞生,异步毫秒级的定时器真的是好用到没得说。

相对与javascript的setInterval和setTimeout,swoole也提供了永久性定时器和一次性定时器,我们分别来看下怎么玩。

二、永久性定时器

所谓的永久性定时器,就是在设定好定时器之后,该定时器就会按照一定的时间间隔执行,直到该定时器被删除。

这种类型的定时器,我们可以使用swoole_timer_tick函数创建,该函数接收3个参数,原型如下

int swoole_timer_tick(int $ms, callable $callback, mixed $params);
  • $ms 指时间,单位毫秒

  • $callback 回调函数,定时器创建后会调用该函数

  • $params 传递给回调函数的参数

即创建一个ms毫秒后执行callback的定时器。

来看一个简单的例子:tick.php

<?php
swoole_timer_tick(1000, function () {
    echo "This is a tick.\n";
});

案例中我们创建了一个永久性定时器,每1000毫秒即每秒执行一次回调函数,输出"This is a tick.\n"。

定时器的清除,可以使用swoole_timer_clear函数操作,该函数接收一个参数,定时器的id,函数原型如下

bool swoole_timer_clear(int $timerId)

再来看一个稍微完整的例子:tick-2.php

<?php

$i = 0;

swoole_timer_tick(1000, function ($timeId, $params) use (&$i) {
    $i ++;
    echo "hello, {$params} --- {$i}\n";
    if ($i >= 5) {
        swoole_timer_clear($timeId);
    }
}, 'world');

事例中我们创建了一个定时器,该定时器每秒执行一次,swoole_timer_tick的第二个参数即回调函数,该函数的参数$timeId是创建的定时器的id, params是swooletimertick的第三个参数传递的值,use闭包中我们取了变量i的地址,在回调函数中,我们对i++处理,当i >= 5的时候,用swoole_timer_clear函数清除了定时器。运行下该文件,我们看看结果

php tick.php
hello, world --- 1
hello, world --- 2
hello, world --- 3
hello, world --- 4
hello, world --- 5

需要说明的是,swoole_timer_tick函数是全局性的,通常情况下是可以在任意地方调用。

另外,如果在事件的回调函数内,我们还可以通过swoole_server->tick函数创建永久性定时器,并使用swoole_server->clearTimer函数清除定时器,比如上面的例子我们可以在回调函数onWorkerStart中这样写

<?php

$serv->set([
    'worker_num' => 2,
]);
$serv->on('WorkerStart', function ($serv, $workerId){
    if ($workerId == 0) {
        $i = 0;
        $params = 'world';
        $serv->tick(1000, function ($timeId) use ($serv, &$i, $params) {
            $i ++;
            echo "hello, {$params} --- {$i}\n";
            if ($i >= 5) {
                $serv->clearTimer($timeId);
            }
        });
    }
});

代码总体上就不分析了,只看一点,为什么在onWorkerStart回调内判断了$workerId是否等于0?

注意到我们开启了两个Worker进程,如果不判断,那么就会在两个Worker进程内各注册一个定时器,实际上也就是我们注册了两个相同的定时器,这是没有必要的。

注:swoole_server->tick等价于swoole_timer_tick,swoole_server->clearTimer等价于swoole_timer_clear。

三、一次性定时器

一次性定时器执行完一次之后,便会自动销毁。这种场景往往是当xxx几秒之后再执行。

同样也有两个函数供我们使用,全局的swoole_timer_after和回调内可调用的swoole_server->after。

前者的参数等同于swoole_timer_tick,只有一点不同,该函数所支持的最大毫秒数是86400000。

同样我们看两个简单的demo:tick-after.php

<?php
swoole_timer_after(3000, function () {
    echo "only once.\n";
});

回调内执行,我们这回举一个在onReceive内为例:tick-server.php

<?php
$serv = new swoole_server('127.0.0.1', 9501);
$serv->set([
    'worker_num' => 2,
]);
$serv->on('WorkerStart', function ($serv, $workerId){
    if ($workerId == 0) {
        $i = 0;
        $params = 'world';
        $serv->tick(1000, function ($timeId) use ($serv, &$i, $params) {
            $i ++;
            echo "hello, {$params} --- {$i}\n";
            if ($i >= 5) {
                $serv->clearTimer($timeId);
            }
        });
    }
});
$serv->on('Connect', function ($serv, $fd) {
});
$serv->on('Receive', function ($serv, $fd, $fromId, $data) {
    $serv->after(3000, function () {
        echo "only once.\n";
    });
});
$serv->on('Close', function ($serv, $fd) {
});
$serv->start();

以后再也不用crontab了,这随随便便简简单单的就实现了定时器的功能,so easy~


阅读 110