免费观看又色又爽又黄的小说免费_美女福利视频国产片_亚洲欧美精品_美国一级大黄大色毛片

怎么理解Laravel定時任務調度機制

本篇內容主要講解“怎么理解Laravel定時任務調度機制”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么理解Laravel定時任務調度機制”吧!

成都創新互聯是網站建設技術企業,為成都企業提供專業的成都網站建設、成都網站制作,網站設計,網站制作,網站改版等技術服務。擁有十余年豐富建站經驗和眾多成功案例,為您定制適合企業的網站。十余年品質,值得信賴!

1. 基本實現邏輯

一個復雜的web系統后臺當中,一定會有很多定時腳本或者任務要跑。

例如爬蟲系統需要定期去爬取一些網站數據,自動還貸系統需要每個月定時對用戶賬戶扣款結算,

會員系統需要定期檢測用戶剩余會員天數以便及時通知續費等等。Linux系統中內置的crontab一般被廣泛地用于跑定時任務

crontab指令解釋

命令行crontab -e進入crontab編輯,把自己要執行的指令編輯好之后保存退出即可生效。

不過本文并不會過多討論crontab的內容,而是要深入分析一下PHP Laravel框架是如何基于crontab封裝出功能更加強大的任務調度(Task Scheduling)模塊。

對于定時任務,我們當然可以每個任務配置一個crontab指令。只不過這樣做的話隨著定時任務的增加,crontab指令也線性增長。

畢竟crontab是一項系統級的配置,在業務中我們為了節約機器,往往對于量不大的多個項目會放在同一臺服務器上,c

rontab指令多了就容易管理混亂,并且功能也不夠靈活強大(無法隨心所欲的停啟、處理任務間依賴關系等)。

對此Laravel的解決方案是只聲明一條crontab,業務中的所有定時任務全都在這一條crontab中做處理和判斷,實現在代碼層面管理任務:

* * * * * php artisan schedule:run >> /dev/null 2>&1

即php artisan schedule:run每分鐘跑一次(crontab的最高頻率),至于業務上的具體任務配置,則注冊于Kernel::schedule()中

class Kernel extends ConsoleKernel
{
    Protected function schedule(Schedule $schedule)
    {
        $schedule->command('account:check')->everyMinute(); // 每分鐘執行一次php artisan account:check 指令
        $schedule->exec('node /home/username/index.js')->everyFifteenMinutes(); //每15分鐘執行一次node /home/username/index.js 命令
        $schedule->job(new MyJob())->cron('1 2 3 10 *'); // 每年的10月3日凌晨2點1分向任務隊列分發一個MyJob任務
    }
}

上述例子中我們可以很清晰的看到系統中注冊了三項定時任務,并且提供了everyMinute, everyFifteenMinutes, daily, hourly等語義化的方法來配置任務周期。

本質上,這些語義化的方法只是crontab表示方式的一個別稱罷了,最終都會轉化為crontab中的表達方式(如 * * * * * 表示每分鐘執行一次)。

如此一來,每分鐘執行一次的php artisan schedule:run指令,會掃描Kernel::schedule中注冊的所有指令并判斷該指令配置的執行周期時候已經到期,

如果到期則推入待執行隊列。最后依次執行所有的指令。

// ScheduleRunCommand::handle函數
public function handle()
{
    foreach ($this->schedule->dueEvents() as $event) {
        if (! $event->filtersPass()) {
            continue;
        }
        $event->run();
    }
}

這里需要注意兩個點,第一、如何判斷指令是否已經Due了該執行了。第二、指令的執行順序問題。

首先,crontab表達式所指定的執行時間,是指絕對時間,而不是相對時間。所以僅僅根據當前時間和crontab表達式,

即可判斷出指令是否已經Due了該執行了。如果想要實現相對時間,那么必須存儲上一次執行的時間,

然后才能進行推算下次執行應該是什么時候。絕對時間和相對時間的區別可以用下面一幅圖概括(crontab的執行時間如圖中左側列表所示)。

Laravel中對于crontab表達式的靜態分析和判斷使用的是cron-expression庫,原理也比較直觀,就是靜態的字符分析比對。

crontab是絕對時間,而非相對時間

第二個問題是執行順序,前面的圖中我們可以看出,如果你在Kernel::schedule方法中注冊了多個任務,

正常情況下它們是順序依次執行的。也就是說必須要等到Task 1執行完成之后,Task 2才會開始執行。

在這種情況下,如果Task 1非常耗時,則會影響到Task 2的按時執行,這一點在開發中是尤其需要注意的。

不過在Kernel::schedule中注冊任務時加上runInBackground即可實現任務的后臺執行,這點我們下文詳細討論。

2. 后臺運行

前文提到的定時任務隊列順序執行的特性,前面的任務執行時間太長會妨礙后面任務的按時執行。

為解決此問題,Laravel中提供了使任務后臺執行的方法runInBackground。如:

// Kernel.php
protected function schedule(Schedule $schedule)
{
    $schedule->command('test:hello') // 執行command命令:php artisan test:hello
    ->cron('10 11 1 * *') // 每月1日的11:10:00執行該命令
    ->timezone('Asia/Shanghai') // 設置時區
    ->before(function(){/*do something*/}) // 前置hook,命令執行前執行此回調
    ->after(function(){/*do something*/}) // 后置鉤子,命令執行完之后執行此回調
    ->runInBackground(); // 后臺運行本命令
    // 每分鐘執行command命令:php artisan test:world
    $schedule->command('test:world')->everyMinute();
}

后臺運行的原理,其實也非常簡單。我們知道在linux系統下,命令行的指令最后加個“&”符號,可以使任務在后臺執行。

runInBackground方法內部原理其實就是讓最后跑的指令后面加了“&”符號。不過在任務改為后臺執行之后,

又有了一個新的問題,即如何觸發任務的后置鉤子函數。因為后置鉤子函數是需要在任務跑完之后立即執行,

所以必須要有辦法監測到后臺運行的任務結束的一瞬間。我們從源代碼中一探究竟

// 構建運行在后臺的command指令
protected function buildBackgroundCommand(Event $event)
{
    $output = ProcessUtils::escapeArgument($event->output);
    $redirect = $event->shouldAppendOutput ? ' >> ' : ' > ';
    $finished = Application::formatCommandString('schedule:finish').' "'.$event->mutexName().'"';
    return $this->ensureCorrectUser($event,
        '('.$event->command.$redirect.$output.' 2>&1 '.(windows_os() ? '&' : ';').' '.$finished.') > '
        .ProcessUtils::escapeArgument($event->getDefaultOutput()).' 2>&1 &'
    );
}

$finished字符串的內容是一個隱藏的php artisan指令,即php artisan schedule:finish <mutex_name>。

該命令被附在了本來要執行的command命令后面,用來檢測并執行后置鉤子函數。

php artisan schedule:finish <mutex_name>的源代碼非常簡單,用mutex_name來唯一標識一個待執行任務,

通過比較系統中注冊的所有任務的mutex_name,來確定需要執行哪個任務的后置函數。代碼如下:

// Illuminate/Console/Scheduling/ScheduleFinishCommand.php
// php artisan schedule:finish指令的源代碼
public function handle()
{
    collect($this->schedule->events())->filter(function ($value) {
        return $value->mutexName() == $this->argument('id');
    })->each->callAfterCallbacks($this->laravel);
}

3. 防止重復

有些定時任務指令需要執行很長時間,而laravel schedule任務最頻繁可以做到1分鐘跑一次。

這也就意味著,如果任務本身跑了1分鐘以上都沒有結束,那么等到下一個1分鐘到來的時候,又一個相同的任務跑起來了。

這很可能是我們不想看到的結果。因此,有必要想一種機制,來避免任務在同一時刻的重復執行(prevent overlapping)。

這種場景非常類似多進程或者多線程的程序搶奪資源的情形,常見的預防方式就是給資源加鎖。

具體到laravel定時任務,那就是給任務加鎖,只有拿到任務鎖之后,才能夠執行任務的具體內容。

Laravel中提供了withoutOverlapping方法來讓定時任務避免重復。具體鎖的實現上,需要實現Illuminate\Console\Scheduling\Mutex.php接口中所定義的三個接口:

interface Mutex
{
    // 實現創建鎖接口
    public function create(Event $event);
    // 實現判斷鎖是否存在的接口
    public function exists(Event $event);
    // 實現解除鎖的接口
    public function forget(Event $event);
}

該接口當然可以自己實現,Laravel也給了一套默認實現,即利用緩存作為存儲鎖的載體(可參考Illuminate\Console\Scheduling\CacheMutex.php文件)。

在每次跑任務之間,程序都會做出判斷,是否需要防止重復,如果重復了,則不再跑任務代碼:

// Illuminate\Console\Scheduling\Event.php
public function run()
{
    // 判斷是否需要防止重復,若需要防重復,并且創建鎖不成功,則說明已經有任務在跑了,這時直接退出,不再執行具體任務
    if ($this->withoutOverlapping && ! $this->mutex->create($this)) {
        return;
    }
    $this->runInBackground?$this->runCommandInBackground($container):$this->runCommandInForeground($container);
}

4. 如何實現30秒任務?

我們知道crontab任務最精細的粒度只能到分鐘級別。那么如果我想實現30s執行一次的任務,

需要如何實現?關于這個問題,stackoverflow上面也有一些討論,有建議說在業務層面實現,自己寫個sleep來實現,示例代碼如下:

public function handle()
{
    runYourCode(); // 跑業務代碼
    sleep(30); // 睡30秒
    runYourCode(); // 再跑一次業務代碼
}

如果runYourCode執行實現不太長的話,上面這個任務每隔1min執行一次,其實相當于runYourCode函數每30秒執行一次。

如果runYourCode函數本身執行時間比較長,那這里的sleep 30秒會不那么精確。

當然,也可以不使用Laravel的定時任務系統,改用專門的定時任務調度開源工具來實現每隔30秒執行一次的功能,

在此推薦一個定時任務調度工具nomad。

如果你確實要用Laravel自帶的定時任務系統,并且又想實現更精確一些的每隔30秒執行一次任務的功能,那么可以結合laravel 的queue job來實現。如下:

public function handle()
{
    $job1 = (new MyJob())->onQueue(“queue-name”);
    $job2 = (new MyJob())->onQueue(“queue-name”)->delay(30);
    dispatch($job1);
    dispatch($job2):
}

class MyJob implement Illuminate\Contracts\Queue\ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
    public function handle()
    {
        runYourCode();
    }
}

通過Laravel 隊列功能的delay方法,可以將任務延時30s執行,因此如果每隔1min,我們都往隊列中dispatch兩個任務,其中一個延時30秒。

另外,把自己要執行的代碼runYourCode寫在任務中,即可實現30秒執行一次的功能。不過這里需要注意的是,這種實現中scheduling的防止重合功能不再有效,

需要自己在業務代碼runYourCode中實現加鎖防止重復的功能。

到此,相信大家對“怎么理解Laravel定時任務調度機制”有了更深的了解,不妨來實際操作一番吧!這里是創新互聯網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

網頁名稱:怎么理解Laravel定時任務調度機制
文章位置:http://m.newbst.com/article0/gpijoo.html

成都網站建設公司_創新互聯,為您提供網站排名網站維護外貿建站品牌網站制作標簽優化虛擬主機

廣告

聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯

微信小程序開發