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