标题:【Swoole系列5.1】毫秒定时器

文章目录
    分类:PHP 标签:Swoole

    毫秒定时器

    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 的操作,是一个标准的定时器,设定后就开始定时执行。

    $tickA = \Swoole\Timer::tick(1000, function($timer_id, $param1){
       static $i = 0;
       echo $param1 . ": ". microtime(true), PHP_EOL;
       $i++;
       if($i == 3){
           \Swoole\Timer::clear($timer_id);
       }
    }, 'A');
    //A: 1641218219.607
    //A: 1641218220.6068
    //A: 1641218221.6071

    \Swoole\Timer 就是 Swoole 提供的定时器类,它的所有操作都是静态方法。


    tick() 方法就是一个定时器方法,第一个参数是定时的秒数,注意,这里是毫秒,所以我们上面的测试代码其实就是 1000 毫秒表示 1 秒。后面的回调函数是每间隔 1 秒后要执行的内容,它有两个回调参数。第一个参数是定时器的 id ,第二个参数是我们可以通过定时器传递给这个回调函数的参数内容。


    比如说,我们给 tick() 方法的第三个参数传递了一个 A ,那么在回调函数中,我们也可以通过参数的方式接收到这个 A 。这个参数内外都是 ... 形式的多参构造,可以不限数量地传递参数。


    接着在回调方法体内部,我们设定了一个计数器 $i ,为什么是一个静态属性呢?之前都讲过的哦。如果不记得了或者没看过我们之前讲过的 【PHP中的static】https://mp.weixin.qq.com/s/vJc2lXnIg7GCgPkrTh_xsw ,那么最好还是再去复习一下哦。


    $i 达到三次以后,正常情况下也是过了 3 秒之后,我们使用一个 \Swoole\Timer::clear() 来清除定时器。

    After

    除了 tick() 方法之外,还有一个和 setTimeout 非常类似的方法,叫做 after() 方法。

    $tickB = \Swoole\Timer::tick(1000, function($timer_id, $param1){
       echo $param1 . ": ". microtime(true), PHP_EOL;
    }, 'B');
    
    \Swoole\Timer::after(5000, function() use($tickB){
       \Swoole\Timer::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() 。

    \Swoole\Timer::tick(1000, function($timer_id, $param1){
       echo $param1 . ": ". microtime(true), PHP_EOL;
    }, 'C');
    \Swoole\Timer::tick(1000, function($timer_id, $param1){
       echo $param1 . ": ". microtime(true), PHP_EOL;
    }, 'D');
    \Swoole\Timer::after(5000, function(){
       echo "After: ". microtime(true), PHP_EOL;
    });
    
    \Swoole\Timer::after(3000, function(){
       \Swoole\Timer::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() 也被一起清除了。

    其它方法

    剩下的东西其实没什么了,只是一些查看定时器内容的方法函数。

    $tickE = \Swoole\Timer::tick(10000, function($timer_id, $param1){
       echo $param1 . ": ". microtime(true), PHP_EOL;
    }, 'E');
    \Swoole\Timer::tick(10000, function($timer_id, $param1){
       echo $param1 . ": ". microtime(true), PHP_EOL;
    }, 'F');
    
    var_dump(\Swoole\Timer::info($tickE));
    //array(5) {
    //  ["exec_msec"]=>
    //  int(10000)
    //  ["exec_count"]=>
    //  int(0)
    //  ["interval"]=>
    //  int(10000)
    //  ["round"]=>
    //  int(0)
    //  ["removed"]=>
    //  bool(false)
    //}
    
    foreach (Swoole\Timer::list() as $timer_id) {
       var_dump(Swoole\Timer::info($timer_id));
       var_dump(Swoole\Timer::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)
    //}
    // ……………………
    // ……………………

    Swoole\Timer::info() 方法打印定时器的信息,包括执行次数、间隔时间、移除信息等。Swoole\Timer::list() 可以输出当前进程中的所有定时器 id 列表。Swoole\Timer::stats() 用于查看定时器的状态信息。


    除了这三个之外,我们还要关注的是 tick()、after()、clear() 都提供了一个函数风格别名,可以直接使用,见到它们的时候可别懵圈了。

    类静态方法

    函数风格别名

    Swoole\Timer::tick()

    swoole_timer_tick()

    Swoole\Timer::after()

    swoole_timer_after()

    Swoole\Timer::clear()

    swoole_timer_clear()

    总结

    今天的内容非常简单,但却非常有用。有了这个定时器,其实我们就能做很多事情了,比如说常驻内存的定时任务。另外能够精确到毫秒级别也是非常强大的一个优势,毕竟速度和性能是 Swoole 区别于传统 PHP 开发的最大亮点。


    测试代码:


    https://github.com/zhangyue0503/swoole/blob/main/5.%E5%85%B6%E5%AE%83/source/5.1%E6%AF%AB%E7%A7%92%E5%AE%9A%E6%97%B6%E5%99%A8.php


    参考文档:


    https://wiki.swoole.com/#/timer

    视频链接

    微信文章地址:https://mp.weixin.qq.com/s/xZ3fRkKUL_wxj5JGioCvtQ

    B站视频地址:https://www.bilibili.com/video/BV1UD4y1b7XH/

    微信视频地址:https://mp.weixin.qq.com/s/fhmTOb_RS6_C5jCIQvV_IQ

    搜索
    关注