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

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

= 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 中这样写

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

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

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

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~

阅读 252