• 【Swoole系列5.1】毫秒定时器


    毫秒定时器

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

    1. $tickA = \Swoole\Timer::tick(1000, function($timer_id, $param1){
    2.    static $i = 0;
    3.    echo $param1 . ": ". microtime(true), PHP_EOL;
    4.    $i++;
    5.    if($i == 3){
    6.        \Swoole\Timer::clear($timer_id);
    7.    }
    8. }, 'A');
    9. //A: 1641218219.607
    10. //A: 1641218220.6068
    11. //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() 方法。

    1. $tickB = \Swoole\Timer::tick(1000, function($timer_id, $param1){
    2.    echo $param1 . ": ". microtime(true), PHP_EOL;
    3. }, 'B');
    4. \Swoole\Timer::after(5000, function() use($tickB){
    5.    \Swoole\Timer::clear($tickB);
    6. });
    7. //B: 1641218178.0143
    8. //B: 1641218179.0137
    9. //B: 1641218180.0136
    10. //B: 1641218181.0131
    11. //B: 1641218182.0136

    在这段代码中,我们先定义了一个 tick() ,然后通过它的返回值获得它的 timer_id 。接着,我们设定了一个 after() ,它表示的经过多长时间之后会执行指定的回调函数。同样,它的参数也是可以设置为毫秒的。我们这里设置的是 5 秒后,执行回调函数中的 clear() 方法去清除上面定义的那个 tick() 。

    after() 只运行一次,就是在指定的时间之后运行,而 tick() 是一直不停地按指定的时间运行。这个其实就是 JS 中 setTimeout 和 setInterval 区别。

    另外,我们还可以通过一个方法一次性地清理所有的 tick() 和 after() 。

    1. \Swoole\Timer::tick(1000, function($timer_id, $param1){
    2.    echo $param1 . ": ". microtime(true), PHP_EOL;
    3. }, 'C');
    4. \Swoole\Timer::tick(1000, function($timer_id, $param1){
    5.    echo $param1 . ": ". microtime(true), PHP_EOL;
    6. }, 'D');
    7. \Swoole\Timer::after(5000, function(){
    8.    echo "After: ". microtime(true), PHP_EOL;
    9. });
    10. \Swoole\Timer::after(3000, function(){
    11.    \Swoole\Timer::clearAll();
    12. });
    13. //C: 1641222879.0361
    14. //D: 1641222879.0362
    15. //D: 1641222880.0356
    16. //C: 1641222880.0357
    17. //C: 1641222881.034
    18. //D: 1641222881.0341

    首先是两个 tick() 会持续运行,接着是一个 after() ,会在五秒后运行,最后一个 after() 则调用了一个 clearAll() 清除所有的定时器。于是,最后的效果就如同注释中的一样,只有上面两个 tick() 运行了三次,之后包含那个等待五秒的 after() 也被一起清除了。

    其它方法

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

    1. $tickE = \Swoole\Timer::tick(10000, function($timer_id, $param1){
    2.    echo $param1 . ": ". microtime(true), PHP_EOL;
    3. }, 'E');
    4. \Swoole\Timer::tick(10000, function($timer_id, $param1){
    5.    echo $param1 . ": ". microtime(true), PHP_EOL;
    6. }, 'F');
    7. var_dump(\Swoole\Timer::info($tickE));
    8. //array(5) {
    9. //  ["exec_msec"]=>
    10. //  int(10000)
    11. //  ["exec_count"]=>
    12. //  int(0)
    13. //  ["interval"]=>
    14. //  int(10000)
    15. //  ["round"]=>
    16. //  int(0)
    17. //  ["removed"]=>
    18. //  bool(false)
    19. //}
    20. foreach (Swoole\Timer::list() as $timer_id) {
    21.    var_dump(Swoole\Timer::info($timer_id));
    22.    var_dump(Swoole\Timer::stats($timer_id));
    23. }
    24. //array(5) {
    25. //  ["exec_msec"]=>
    26. //  int(10000)
    27. //  ["exec_count"]=>
    28. //  int(0)
    29. //  ["interval"]=>
    30. //  int(10000)
    31. //  ["round"]=>
    32. //  int(0)
    33. //  ["removed"]=>
    34. //  bool(false)
    35. //}
    36. //array(3) {
    37. //  ["initialized"]=>
    38. //  bool(true)
    39. //  ["num"]=>
    40. //  int(2)
    41. //  ["round"]=>
    42. //  int(0)
    43. //}
    44. // ……………………
    45. // ……………………

    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

  • 相关阅读:
    Opengl ES之矩阵变换(上)
    实现SSM项目在服务器的自动化部署(包括jdk安装,入门级教程简单易懂)
    c++使用外部函数备忘
    Python数据分析-5
    架构篇(四) 高可用系统设计
    JavaScript 中的判等
    spring data mongo MongoTemplate 查询最大值的数据
    【计算机网络】socket
    难顶,面试官问我G1垃圾收集器
    后台管理-----搜索框重置
  • 原文地址:https://blog.csdn.net/zhangyue0503/article/details/126825501