一、前言
说起定时器,大家都不陌生。我最早接触定时器的概念,是 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
$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 中这样写
$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
swoole_timer_after(3000, function () {
echo "only once.\n";
});
回调内执行,我们这回举一个在 onReceive 内为例:tick-server.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~