毫秒定时器
PHP 中有没有定时器?还记得我们之前讲过这个东西吧。如果不记得的小伙伴,可以移步之前的文章中再去重温一下 PHP没有定时器?PHP没有定时器 https://mp.weixin.qq.com/s/NIYwhVLRl0drIcRvIoWvJA 。当时我们实现的方法是使用 declare ,今天,我们要学习的,则是 Swoole 提供的一套定时器工具。
Timer 定时器
Swoole 中提供的这个 Timer 定时器,底层是基于 epoll_wait 和 settitimer 实现的,数据结构使用最小堆,全部为内存操作,没有 IO 消耗。如果这些名词搞不明白的话,那么就记住最后一句话,它的性能非常高。
官方有提供一个基准测试脚本,添加或删除 10 万个随机时间的定时器耗时仅为 0.08s 左右,性能非常强悍。如果要与我们之前讲过的 declare 对比的话,那么这个 Swoole 提供的定时器,能够支持到毫秒级别、能够同时设定多个定时器,性能也更强悍。
说了这么多,其实我们就早就用过定时器了。在讲进程的时候,为了挂起不阻塞的 wait 监听,我们用得是什么?还记得吗?【Swoole系列3.3】单进程管理Processhttps://mp.weixin.qq.com/s/PulzY6Z0cXZocLuT-bqn2w 。
定时器使用
讲了那么多好处,这个定时器怎么用呢?它与 JS 的 setInterval 以及 setTimeout 非常类似,也分为两种操作。
Tick
Tick 操作就是类似于 setInterval 的操作,是一个标准的定时器,设定后就开始定时执行。
代码语言:javascript复制$tickA = SwooleTimer::tick(1000, function($timer_id, $param1){
static $i = 0;
echo $param1 . ": ". microtime(true), PHP_EOL;
$i ;
if($i == 3){
SwooleTimer::clear($timer_id);
}
}, 'A');
//A: 1641218219.607
//A: 1641218220.6068
//A: 1641218221.6071
SwooleTimer 就是 Swoole 提供的定时器类,它的所有操作都是静态方法。
tick() 方法就是一个定时器方法,第一个参数是定时的秒数,注意,这里是毫秒,所以我们上面的测试代码其实就是 1000 毫秒表示 1 秒。后面的回调函数是每间隔 1 秒后要执行的内容,它有两个回调参数。第一个参数是定时器的 id ,第二个参数是我们可以通过定时器传递给这个回调函数的参数内容。
比如说,我们给 tick() 方法的第三个参数传递了一个 A ,那么在回调函数中,我们也可以通过参数的方式接收到这个 A 。这个参数内外都是 ... 形式的多参构造,可以不限数量地传递参数。
接着在回调方法体内部,我们设定了一个计数器 $i
,为什么是一个静态属性呢?之前都讲过的哦。如果不记得了或者没看过我们之前讲过的 【PHP中的static】https://mp.weixin.qq.com/s/vJc2lXnIg7GCgPkrTh_xsw ,那么最好还是再去复习一下哦。
当 $i
达到三次以后,正常情况下也是过了 3 秒之后,我们使用一个 SwooleTimer::clear() 来清除定时器。
After
除了 tick() 方法之外,还有一个和 setTimeout 非常类似的方法,叫做 after() 方法。
代码语言:javascript复制$tickB = SwooleTimer::tick(1000, function($timer_id, $param1){
echo $param1 . ": ". microtime(true), PHP_EOL;
}, 'B');
SwooleTimer::after(5000, function() use($tickB){
SwooleTimer::clear($tickB);
});
//B: 1641218178.0143
//B: 1641218179.0137
//B: 1641218180.0136
//B: 1641218181.0131
//B: 1641218182.0136
在这段代码中,我们先定义了一个 tick() ,然后通过它的返回值获得它的 timer_id 。接着,我们设定了一个 after() ,它表示的经过多长时间之后会执行指定的回调函数。同样,它的参数也是可以设置为毫秒的。我们这里设置的是 5 秒后,执行回调函数中的 clear() 方法去清除上面定义的那个 tick() 。
after() 只运行一次,就是在指定的时间之后运行,而 tick() 是一直不停地按指定的时间运行。这个其实就是 JS 中 setTimeout 和 setInterval 区别。
另外,我们还可以通过一个方法一次性地清理所有的 tick() 和 after() 。
代码语言:javascript复制SwooleTimer::tick(1000, function($timer_id, $param1){
echo $param1 . ": ". microtime(true), PHP_EOL;
}, 'C');
SwooleTimer::tick(1000, function($timer_id, $param1){
echo $param1 . ": ". microtime(true), PHP_EOL;
}, 'D');
SwooleTimer::after(5000, function(){
echo "After: ". microtime(true), PHP_EOL;
});
SwooleTimer::after(3000, function(){
SwooleTimer::clearAll();
});
//C: 1641222879.0361
//D: 1641222879.0362
//D: 1641222880.0356
//C: 1641222880.0357
//C: 1641222881.034
//D: 1641222881.0341
首先是两个 tick() 会持续运行,接着是一个 after() ,会在五秒后运行,最后一个 after() 则调用了一个 clearAll() 清除所有的定时器。于是,最后的效果就如同注释中的一样,只有上面两个 tick() 运行了三次,之后包含那个等待五秒的 after() 也被一起清除了。
其它方法
剩下的东西其实没什么了,只是一些查看定时器内容的方法函数。
代码语言:javascript复制$tickE = SwooleTimer::tick(10000, function($timer_id, $param1){
echo $param1 . ": ". microtime(true), PHP_EOL;
}, 'E');
SwooleTimer::tick(10000, function($timer_id, $param1){
echo $param1 . ": ". microtime(true), PHP_EOL;
}, 'F');
var_dump(SwooleTimer::info($tickE));
//array(5) {
// ["exec_msec"]=>
// int(10000)
// ["exec_count"]=>
// int(0)
// ["interval"]=>
// int(10000)
// ["round"]=>
// int(0)
// ["removed"]=>
// bool(false)
//}
foreach (SwooleTimer::list() as $timer_id) {
var_dump(SwooleTimer::info($timer_id));
var_dump(SwooleTimer::stats($timer_id));
}
//array(5) {
// ["exec_msec"]=>
// int(10000)
// ["exec_count"]=>
// int(0)
// ["interval"]=>
// int(10000)
// ["round"]=>
// int(0)
// ["removed"]=>
// bool(false)
//}
//array(3) {
// ["initialized"]=>
// bool(true)
// ["num"]=>
// int(2)
// ["round"]=>
// int(0)
//}
// ……………………
// ……………………
SwooleTimer::info() 方法打印定时器的信息,包括执行次数、间隔时间、移除信息等。SwooleTimer::list() 可以输出当前进程中的所有定时器 id 列表。SwooleTimer::stats() 用于查看定时器的状态信息。
除了这三个之外,我们还要关注的是 tick()、after()、clear() 都提供了一个函数风格别名,可以直接使用,见到它们的时候可别懵圈了。
类静态方法 | 函数风格别名 |
---|---|
SwooleTimer::tick() | swoole_timer_tick() |
SwooleTimer::after() | swoole_timer_after() |
SwooleTimer::clear() | swoole_timer_clear() |
总结
今天的内容非常简单,但却非常有用。有了这个定时器,其实我们就能做很多事情了,比如说常驻内存的定时任务。另外能够精确到毫秒级别也是非常强大的一个优势,毕竟速度和性能是 Swoole 区别于传统 PHP 开发的最大亮点。
测试代码:
https://github.com/zhangyue0503/swoole/blob/main/5.其它/source/5.1毫秒定时器.php
参考文档:
https://wiki.swoole.com/#/timer