比如我有很多定時器,有指定時間執(zhí)行的比如每天0點28分執(zhí)行【28 0 】,有固定間隔時間段執(zhí)行的比如每五分鐘執(zhí)行異常【0 /1 】,還可以動態(tài)的添加管理這些定時器
有參考過插件市場的yzh52521/webman-task,啟動兩個服務,一個提供http請求處理添加刪除操作,一個啟動自定義進程來實現定時任務的執(zhí)行,在內部http和自定義進程通訊來進行數據交互
但是原生的Crontab定時任務是阻塞執(zhí)行的,如果有一個執(zhí)行很久會導致剩下的不能再對應時間執(zhí)行,阻塞期間http服務與自定義進程之間的通訊還會掛起
然后試過使用swoole做事件循環(huán),在new Crontab的callback中使用go(function(){}),這樣確實可以實現不阻塞的執(zhí)行定時程序,也不會阻塞自定義進程的通訊服務,但是隨著定時程序的數量、耗時增多,后面查看日志會發(fā)現時不時的exit with status 11、exit with status 65280報錯,然后進程自動重啟,在runtimes下面也看不到具體的報錯日志
后面又試過直接使用swoole開啟tcp服務,在workerStart里面初始化定時器,在receive里面接受http的請求來管理定時器執(zhí)行,因為swoole不支持linux的crontab表達式,webman的實現是生成一個回調函數,拿到下一分鐘要執(zhí)行的定時任務依次Timer::add生成定時器,我把Timer::add替換成\Swoole\Timer::after就可以在swoole中使用crontab表達式,執(zhí)行的時候也是Co::create()執(zhí)行的,然后發(fā)現定時器可以正常按時間執(zhí)行,但是定時器執(zhí)行后tcp服務就被掛起了,無法和http交互
請問各位大佬有類似實現過對應功能嗎,怎么解決支持秒級的定時器執(zhí)行,并且可以進行通訊、定時任務執(zhí)行期間不阻塞服務的
架構一般是一個定時進程,一堆http業(yè)務進程。
定時進程負責定時觸發(fā)任務,使用workerman/http-client異步調用,這樣定時進程不會有任何阻塞操作。
http業(yè)務進程負責處理具體的定時任務,任務本身是否阻塞沒有太大影響,只要http進程足夠就行。
使用php協程,參考文檔
http://www.wtbis.cn/a/1723
https://ripple.cloudtay.com/docs/basic/defer/
在定時器里面用協程執(zhí)行任務,定時器會立即返回,開始執(zhí)行下一個定時器,并不會阻塞,這樣解決了多個定時器互相阻塞的問題
后續(xù)還是用swoole實現了,swoole開啟的tcp服務設置運行協程和一鍵協程化之后,tcp的通訊服務和定時任務就互不阻塞了。其中最主要的問題還是用swoole作為webman的eventloop之后,看起來是互相沖突導致webman子進程重啟,沒這個問題的話兩個一起用還是挺方便的
$server = new Server($host, (int)$port, SWOOLE_PROCESS);
// 設置服務器為異步非阻塞模式
$server->set([
'worker_num' => 1,
'enable_coroutine' => true,
'max_coroutine' => 3000,
'daemonize' => true,
'log_file' => runtime_path() . '/logs/swoole.log',
'log_date_format' => '%Y-%m-%d %H:%M:%S',
'log_rotation' => SWOOLE_LOG_ROTATION_DAILY,
'display_errors' => true,
'hook_flags' => SWOOLE_HOOK_ALL,
]);
// 當有客戶端數據到達時
$server->on('receive', function (Server $server, $fd, $reactor_id, $data) {
Co::create(function () use ($server, $fd, $data) {
// echo "Received data from client {$fd}: {$data}\n";
try {
$data = json_decode($data, true);
$method = $data['method']?:'';
$args = $data['args']?:[];
$res = $this->onReceive($method,$args);
$server->send($fd,$res);
}catch (\Exception $exception){
$server->send($fd,json_encode(['code' => 0, 'msg' => $exception->getMessage() ]));
}
});
});
插件市場:http://www.wtbis.cn/app/view/cron
最關鍵的邏輯是使用composer require symfony/process
你應該已經完成??吹竭@個想到上家公司自己做的定時調度,感覺也挺好。用ai把我記憶中上家公司的實現流程總結出來了。供參考:
你的方案是可行的,尤其是結合了多進程、定時調度、任務狀態(tài)管理以及異步執(zhí)行的設計,符合大多數任務調度系統(tǒng)的最佳實踐。以下是對你的方案的總結和優(yōu)化建議:
symfony/process
啟動多個子進程,讓每個任務在獨立的進程中執(zhí)行,避免任務之間的相互阻塞或影響。composer
包(例如 mtdowling/cron-expression
)來解析任務的 crontab 表達式,從而決定任務的下次執(zhí)行時間。這可以保證靈活的任務調度。swoole
提供的協程來進一步優(yōu)化任務的異步執(zhí)行,尤其是涉及網絡請求、數據庫 I/O 等操作時,協程能有效提升任務的執(zhí)行效率,避免阻塞。RabbitMQ
、Redis
)進一步解耦任務調度和執(zhí)行,確保任務的異步處理和高效并發(fā)。SETNX
來加鎖任務,確保每個任務在同一時刻只會被一個進程處理。任務執(zhí)行完畢后,釋放 Redis 鎖。SELECT ... FOR UPDATE
)或文件鎖(flock
)來加鎖任務。symfony/process
或 swoole
的超時機制確保任務不會長時間占用資源,任務執(zhí)行時間超過設定的超時時間后自動終止,并標記為“超時”狀態(tài)。任務鎖定的策略:
負載均衡與擴展:
監(jiān)控與告警:
Prometheus
、Grafana
)來監(jiān)控任務的執(zhí)行狀態(tài)與性能,尤其在任務超時、失敗等異常情況下,觸發(fā)告警通知以便及時處理。總體來說,你的方案充分考慮了定時調度、任務并發(fā)、異步執(zhí)行、任務狀態(tài)管理和日志記錄,具備較強的擴展性和魯棒性。通過適當的鎖機制、進程管理與協程結合,能夠實現一個高效、穩(wěn)定的任務調度系統(tǒng)。