常規(guī)定時器基于linux的crontab來實現(xiàn),無法滿足毫秒級、秒級處理任務(wù)的場景。
swoole提供了類似JavaScript的setInterval/setTimeout異步高精度定時器Timer,粒度為毫秒級。使用也非常簡單。
在同步進程中使用setitimer和信號實現(xiàn),如Manager和TaskWorker進程;
在異步進程中使用epoll_wait/kevent/poll/select超時時間實現(xiàn)。
函數(shù)列表
//每隔2000ms觸發(fā)一次
swoole_timer_tick(2000, function ($timer_id) {
echo "tick-2000ms\n";
});
//3000ms后執(zhí)行此函數(shù)
swoole_timer_after(3000, function () {
echo "after 3000ms.\n";
});
swoole_timer_tick函數(shù)就相當(dāng)于setInterval,是持續(xù)觸發(fā)的
swoole_timer_after函數(shù)相當(dāng)于setTimeout,僅在約定的時間觸發(fā)一次
swoole_timer_tick和swoole_timer_after函數(shù)會返回一個整數(shù),表示定時器的ID
可以使用 swoole_timer_clear 清除此定時器,參數(shù)為定時器ID

性能
底層使用最小堆數(shù)據(jù)結(jié)構(gòu)實現(xiàn)定時器,定時器的添加和刪除,全部為內(nèi)存操作,因此性能是非常高的。官方的基準測試腳本 https://github.com/swoole/swoole-src/blob/master/benchmark/timer.php 中,添加或刪除10萬個隨機時間的定時器耗時為0.08s左右。
定時器是內(nèi)存操作,無IO消耗
~/workspace/swoole/benchmark$ php timer.php
add 100000 timer :0.091133117675781s
del 100000 timer :0.084658145904541s
差異
Timer與PHP本身的pcntl_alarm是不同的。pcntl_alarm是基于時鐘信號 + tick函數(shù)實現(xiàn)存在一些缺陷:
最大僅支持到秒,而Timer可以到毫秒級別
不支持同時設(shè)定多個定時器程序
pcntl_alarm依賴declare(ticks = 1),性能很差
零毫秒定時器
底層不支持時間參數(shù)為0的定時器。這與Node.js等編程語言不同。在Swoole里可以使用Swoole\Event::defer實現(xiàn)類似的功能。
Swoole\Event::defer(function () {
echo "hello\n";
});
上述代碼與JS中的setTimeout(func, 0)效果是完全一致的。
示例
啟動HTTP服務(wù)

啟動WebSocket服務(wù)

在控制臺可以看到,客戶端先接收到服務(wù)端返回的日期,5秒后在接收到服務(wù)端返回的server-time-after字符串。
但是在服務(wù)端代碼中,返回日期的代碼邏輯是寫在返回server-time-after字符串的后邊的,這是因為swoole實現(xiàn)了task任務(wù)的異步處理,不用等5秒邏輯執(zhí)行完才執(zhí)行后邊的邏輯。
1、當(dāng)websocket服務(wù)端連接到websocket客戶端后,每隔兩秒輸出一次定時器ID。

2、Websocket服務(wù)端接收到客戶端的數(shù)據(jù)5秒后打印5s-after,并給客戶端發(fā)送消息。


注意:在swoole_timer_after()函數(shù)中使用到了PHP的閉包,不懂可以查閱官方文檔。