Timer定時器
框架對Swoole
毫秒級定時器進行了封裝,方便開發者快速上手。
注意??:定時器傳入的時間參數為毫秒,若開啟了 reload_async
配置時,請將定時器移動到自定義進程內,否則會導致worker
進程無法reload
。
循環執行
設置一個間隔時鐘定時器,每隔一定的時間定時觸發,直到進行 clear
操作才會停止,對應 Swoole
原生的定時器函數為 swoole_timer_tick
函數原型
/**
* 循環調用
* @param int $ms 循環執行的間隔毫秒數 傳入整數型
* @param \Closure $callback 定時器需要執行的操作 傳入一個閉包
* @param string $name 定時器名稱,用于取消該定時器
* @param mixed ...$params 傳入定時器的參數
* @return int 返回整數型的定時器編號 可以用該編號停止定時器
*/
public function loop(int $ms, callable $callback, $name = null, ...$params)
示例代碼
// 每隔 10 秒執行一次
\EasySwoole\Component\Timer::getInstance()->loop(10 * 1000, function () {
echo "this timer runs at intervals of 10 seconds\n";
});
延時執行
設置一個延時定時器,延時指定的時間后觸發對應的操作,只會執行一次操作,對應Swoole
原生的定時器函數為 swoole_timer_after
函數原型
/**
* 延時調用
* @param int $ms 需要延遲執行的時間
* @param \Closure $callback 定時器需要執行的操作 傳入一個閉包
* @param mixed ...$params 傳入定時器的參數
* @return int 返回整數型的定時器編號
*/
public function after(int $ms, callable $callback, ...$params)
示例代碼
// 10 秒后執行一次
\EasySwoole\Component\Timer::getInstance()->after(10 * 1000, function () {
echo "ten seconds later\n";
});
清除定時器
注意: 該操作不能用于清除其他進程的定時器,只作用于當前進程
定時器創建成功時,會返回一個整數型編號,調用本函數傳入該編號,即可提前停止定時器,對應 Swoole
原生的定時器函數為 swoole_timer_clear
函數原型
/**
* 清除定時器
* @param mixed $timerIdOrName 定時器編號或名稱
* @return bool
*/
public function clear($timerIdOrName)
示例代碼
// 創建一個2秒定時器
$timerId = \EasySwoole\Component\Timer::getInstance()->loop(2 * 1000, function () {
echo "timeout\n";
},'time');
// 清除該定時器
var_dump(\EasySwoole\Component\Timer::getInstance()->clear($timerId)); // bool(true)
var_dump($timerId); // int(1)
// 定時器得不到執行 不輸出:timeout
應用實例
注意:定時器不能在服務啟動之前使用。在服務啟動以后,添加的定時器僅在當前進程中有效。在 WorkerStart
事件中添加定時器時,請注意判斷需要添加定時器的workerId
,否則該定時器在每個進程中均會被執行。
// 為第一個 Worker 添加定時器
if ($workerId == 0) {
\EasySwoole\Component\Timer::getInstance()->loop(10 * 1000, function () {
echo "timer in the worker number 0\n";
});
}
public static function mainServerCreate(EventRegister $register)
{
$register->add(EventRegister::onWorkerStart, function (\swoole_server $server, $workerId) {
//如何避免定時器因為進程重啟而丟失
//例如在第一個進程 添加一個10秒的定時器
if ($workerId == 0) {
\EasySwoole\Component\Timer::getInstance()->loop(10 * 1000, function () {
// 從數據庫,或者是redis中,去獲取下個就近10秒內需要執行的任務
// 例如:2秒后一個任務,3秒后一個任務 代碼如下
\EasySwoole\Component\Timer::getInstance()->after(2 * 1000, function () {
//為了防止因為任務阻塞,引起定時器不準確,把任務給異步進程處理
Logger::getInstance()->console("time 2", false);
});
\EasySwoole\Component\Timer::getInstance()->after(3 * 1000, function () {
//為了防止因為任務阻塞,引起定時器不準確,把任務給異步進程處理
Logger::getInstance()->console("time 3", false);
});
});
}
});
}
經典案例-訂單狀態超時監控
場景說明:在很多搶購的場景中,訂單下單完成后,需要限制其付款時間,或者是在棋牌游戲中,需要對房間狀態進行監控。那么我們可以先把待監控的訂單或者是房間壓入redis
隊列中。那么就可以利用 定時器 + 異步進程
,去實現對訂單狀態的循環監控。