Swoole 定时器简介
Swoole 提供了异步高精度定时器功能,该功能类似 JavaScript 的 setInterval/setTimeout,粒度为毫秒级,底层基于 epoll_wait(异步)和 setitimer(同步)实现,数据结构使用最小堆。定时器的添加和删除,全部为内存操作,无 IO 消耗,因此性能是非常高的。
需要注意的是,Swoole 实现的定时器与 PHP 自带的 pcntl_alarm不同,pcntl_alarm 是基于时钟信号和 tick 函数实现的,最大仅支持到秒,此外不支持同时设定多个定时器程序,性能也很差。
Swoole 提供了两种类型的定时器,一种是每隔一定时间执行的定时器,一种是指定时间后一次性执行的定时器,下面我们分别看看这两种定时器的实现和使用。
Swoole 定时器的使用
间隔时钟定时器
在 Swoole 中,我们可以通过 Timer::tick 实现间隔时钟定时器,该定时器会每隔指定时间触发回调函数的执行:
SwooleTimer::tick(1000, function () { echo "Swoole 很棒";});
Swoole 定时器需要在 PHP CLI 模式下才能运行,上述代码的意思是每隔 1000ms 执行一次回调函数打印字符串:
![e955c6fdc857d90f98665bfe0444d55b.png](https://i-blog.csdnimg.cn/blog_migrate/3d79df046536cebd5a6b41907b8faa90.jpeg)
指定时钟定时器
此外,还可以通过 Timer::after 定义一个指定时间后执行的定时器,与间隔时钟定时器不同,这种定时器是一次性的,执行完成后就会销毁:
SwooleTimer::after(3000, function () { echo "Laravel 也很棒";});
上述定义器的含义是 3000ms 后执行指定的回调函数,并且执行之后就退出程序:
![7154d16ffb570a10900e489a665b22e5.png](https://i-blog.csdnimg.cn/blog_migrate/8e8595c97ccc28bc5a341352c0356eeb.jpeg)
清除定时器
对于一次性执行的指定时钟定时器,不用关心清除问题,而对于间隔时钟定时器,如果不定义清楚逻辑的话,会永远执行下去,直到程序退出,我们可以通过 Timer::clear 删除定时器来达到清除的目的,在具体实现的时候,将定时器 ID 传入该方法即可,上述两种定时器定义方法都会返回对应的定时器 ID, 因此,清除定义器可以作用于上述两种定时器:
/*$timerId = SwooleTimer::tick(1000, function () { echo "Swoole 很棒";});*/$timerId = SwooleTimer::after(1000, function () { echo "Laravel 也很棒";});SwooleTimer::clear($timerId);
这种情况下,两个定时器都不会调用,对于间隔时钟定时器,还可以这么清除:
$count = 0;SwooleTimer::tick(1000, function ($timerId, $count) { global $count; echo "Swoole 很棒"; $count++; if ($count == 3) { SwooleTimer::clear($timerId); }}, $count);
这种情况下,定时器会在执行三次后退出:
![1fe0b71542aa22802175ba2c756ffc48.png](https://i-blog.csdnimg.cn/blog_migrate/9549a9d14563c9af113c350700f2873c.jpeg)
基于 Swoole 定时器实现毫秒级任务调度
我们可以基于 Swoole 定时器实现毫秒级调度任务来替代 Linux 自带的 Cron Job(最小粒度是分钟),以 Laravel 框架为例,我们可以基于LaravelS扩展包来定义一个继承自 CronJob 基类的调度任务类:
i, microtime(true)]); $this->i++; Log::info(__METHOD__, ['end', $this->i, microtime(true)]); if ($this->i == 3) { // 总共运行3次 Log::info(__METHOD__, ['stop', $this->i, microtime(true)]); $this->stop(); // 清除定时器 } } // 每隔 1000ms 执行一次任务 public function interval() { return 1000; // 定时器间隔,单位为 ms } // 是否在设置之后立即触发 run 方法执行 public function isImmediate() { return false; }}
然后到 config/laravels.php 配置文件中修改 timer 配置项如下:
'timer' => [ 'enable' => true, 'jobs' => [ // Enable LaravelScheduleJob to run `php artisan schedule:run` every 1 minute, replace Linux Crontab // Hhxsv5LaravelSIlluminateLaravelScheduleJob::class, // Two ways to configure parameters: // [AppJobsXxxCronJob::class, [1000, true]], // Pass in parameters when registering AppJobsTimerTestCronJob::class, // Override the corresponding method to return the configuration ], 'max_wait_time' => 5, // Max waiting time of reloading],
接下来,我们启动或重启 Swoole 服务器:
php bin/laravels start
在 storage/logs 下的最新日志里就可以看到上述定时任务的输出了:
[2019-05-29 22:37:20] local.INFO: AppJobsTimerTestCronJob::run ["start