laravel 異步監控定時調度器

定時調度器是什麼

laravel默認提供了一個命令定時任務的功能,在其餘的php框架下面,沒有這個定時任務,咱們要跑一些異步腳本怎麼操做呢,只能依賴咱們系統提供的crontab來作,這就致使咱們每次發版本新增定時任務都要去服務器更改crontab代碼,獲取更新這個配置。php

執行命令是php artisan schedule:run 來執行,那放在哪裏執行呢,沒錯這個調起仍是須要依賴咱們crontab來執行,可是隻須要配置一次,後續全部定時任務都在咱們業務代碼進行控制linux

場景

咱們有一個導入數據的定時任務laravel

//每分鐘導入庫數據
$schedule->command(self::SIGNATURE)->withoutOverlapping()->everyMinute()->runInBackground();
複製代碼
  1. 這裏導入長時間最好使用runInBackground(),表示異步執行,其實就是在shell腳本的末尾加上 & 符號,在linux上徹底依賴系統的方式完成。
  2. 這裏使用了withoutOverlapping() 表示在同一時刻只能有一個任務執行,主要邏輯使用的是排它鎖實現,依賴於咱們cache的driver,我這裏使用的是redis,後面做爲鎖的過時直接redis提供的key過時來作。

出現問題

這個任務在正常狀況下都是很是完美的,由於同一時刻只有一個再跑,跑完就能夠,可是一個場景出現git

有一天咱們的qa同窗剛部署環境後,咱們服務端就在默默的導入庫了,由於使用withoutOverlapping($expire_at=1440)這個時候在redis就有一個鎖產生了,這個默認帶參數是鎖的過時時間,默認是一天,而後由於咱們docker環境須要更改參數而後進行後端server服務的重啓,咱們重啓也是比較暴力,就是直接發送kill的信號,致使全部在裏面跑的進程瞬間kill,而這時候咱們的redis的鎖缺還存在,並且是1440分鐘左右,那當咱們server再啓動後,發現鎖一直存在,沒辦法進行後續的操做了,只能等着。github

解決

  1. 那我把鎖的時間減小行不行,原來1天,我改爲30分鐘,沒問題,開始初版方案咱們也是這樣作,官方也是能夠這樣作的。
  2. 後來咱們一想,可否作到一個監控程序呢,進程退出後立馬監控到過時呢,這樣就不用固定一個時間,這固然是全部軟件開發者理想狀態:要你開你就開,我掛了鎖也就去掉了,不論正常與否。

解決方案

說明:redis

  1. 這裏命令啓動時候,獲取進程的pid,而後fork子進程,能夠將這個pid傳遞給子進程。
  2. 子進程每隔10s進行一個探活,獲取父進程的id與傳入的pid是否一致,這裏普及一個知識點,若是父進程異常退出,這個子進程未退出就會被init進程(pid=1)接管,那麼這就是一個孤兒進程。
  3. 同時子進程每次探活的時候就會更改redis的鎖的過時時間,若是探活時間間隔是10s,那麼咱們的過時時間設置就是14s,多冗餘一點時間。

代碼實現

代碼實現老是那麼蒼白無力哈,這裏就寫一個laravel的擴展來作,好處就是不影響咱們主體的任何代碼就完成了,咱們的laravel能夠隨意升級。docker

github地址:github.com/zzh78727258…shell

composer地址:packagist.org/packages/ze…後端

總結

  1. 總體實現沒有使用判斷進程是否存在的ps grep等命令,由於咱們docker環境不必定支持這些命令,只是用簡單的pid與parent_id作對比。
  2. laravel的在命令開始於結束都進行鉤子方式,咱們在Listener下面進行監聽便可
public function subscribe($events)
    {
        $events->listen(
            [
                CommandStarting::class, // 命令開始的時候
            ],
            __CLASS__ . '@handle'
        );
    }
複製代碼
  1. 總體代碼是基於laravel擴展化的,不會影響laravel的升級操做。
相關文章
相關標籤/搜索