Laravel 服務之任務排程

Fjun發表於2017-05-06

1、簡介

在以前,開發者需要為每一個需要排程的任務編寫一個Cron條目,這是很讓人頭疼的事。你的任務排程不在原始碼控制中,你必須使用SSH登入到伺服器然後新增這些Cron條目。Laravel命令排程器允許你平滑而又富有表現力地在Laravel中定義命令排程,並且伺服器上只需要一個Cron條目即可。

任務排程定義在app/Console/Kernel.php檔案的schedule方法中,該方法中已經包含了一個示例。你可以自由地新增你需要的排程任務到Schedule物件。

1.1 開啟排程

下面是你唯一需要新增到伺服器的Cron條目:

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

2、定義排程

你可以在App\Console\Kernel類的schedule方法中定義所有排程任務。開始之前,讓我們看一個排程任務的例子,在這個例子中,我們將會在每天午夜排程一個被呼叫的閉包。在這個閉包中我們將會執行一個資料庫查詢來清空表:

<?php

namespace App\Console;

use DB;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel{
    /**
     * 應用提供的Artisan命令
     *
     * @var array
     */
    protected $commands = [
        'App\Console\Commands\Inspire',
    ];

    /**
     * 定義應用的命令排程
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->call(function () {
            DB::table('recent_users')->delete();
        })->daily();
    }
}

除了排程閉包呼叫外,還可以排程Artisan命令和作業系統命令。例如,可以使用command方法來排程一個Artisan命令:

$schedule->command('emails:send --force')->daily();

exec命令可用於傳送命令到作業系統:

$schedule->exec('node /home/forge/script.js')->daily();

2.1 排程常用選項

當然,你可以分配多種排程到任務:

方法 描述
->cron(' *** '); 在自定義Cron排程上執行任務
->everyMinute(); 每分鐘執行一次任務
->everyFiveMinutes(); 每五分鐘執行一次任務
->everyTenMinutes(); 每十分鐘執行一次任務
->everyThirtyMinutes(); 每三十分鐘執行一次任務
->hourly(); 每小時執行一次任務
->daily(); 每天凌晨零點執行任務
->dailyAt('9:00'); 每天9:00執行任務
->twiceDaily(1, 9); 每天1:00 & 9:00執行任務
->weekly(); 每週執行一次任務
->monthly(); 每月執行一次任務

這些方法可以和額外的約束一起聯合起來建立一週特定時間執行的更加細粒度的排程,例如,要每週一排程一個命令:

$schedule->call(function () {
    // 每週星期一9:00執行一次...
})->weekly()->mondays()->at('9:00');

下面是額外的排程約束列表:

方法 描述
->weekdays(); 只在工作日執行任務
->sundays(); 每個星期天執行任務
->mondays(); 每個星期一執行任務
->tuesdays(); 每個星期二執行任務
->wednesdays(); 每個星期三執行任務
->thursdays(); 每個星期四執行任務
->fridays(); 每個星期五執行任務
->saturdays(); 每個星期六執行任務
->when(Closure); 基於特定測試執行任務

2.1.1 基於測試的約束條件

when方法用於限制任務在通過給定測試之後執行。換句話說,如果給定閉包返回true,只要沒有其它約束條件阻止任務執行,該任務就會執行:

$schedule->command('emails:send')->daily()->when(function () {
    return true;
});

2.2 避免任務重疊

預設情況下,即使前一個任務仍然在執行排程任務也會執行,要避免這樣的情況,可使用withoutOverlapping方法:

$schedule->command('emails:send')->withoutOverlapping();

在本例中,Artisan命令emails:send每分鐘都會執行,如果該命令沒有在執行的話。如果你的任務在執行時經常大幅度的變化,那麼withoutOverlapping方法就非常有用,你不必再去預測給定任務到底要消耗多長時間。


3、任務輸出

Laravel排程器為處理排程任務輸出提供了多個方便的方法。首先,使用sendOutputTo方法,你可以傳送輸出到檔案以便稍後檢查:

$schedule->command('emails:send')
         ->daily()
         ->sendOutputTo($filePath);

使用emailOutputTo方法,你可以將輸出傳送到電子郵件,注意輸出必須首先通過sendOutputTo方法傳送到檔案。還有,使用電子郵件傳送任務輸出之前,應該配置Laravel的電子郵件服務

$schedule->command('foo')
         ->daily()
         ->sendOutputTo($filePath)
         ->emailOutputTo('foo@example.com');

注意:emailOutputTo和sendOutputTo方法只對command方法有效,不支援call方法。


4、任務鉤子

使用beforeafter方法,你可以指定在排程任務完成之前和之後要執行的程式碼:

$schedule->command('emails:send')
         ->daily()
         ->before(function () {
             // Task is about to start...
         })
         ->after(function () {
             // Task is complete...
         });

4.1 ping URL

使用pingBeforethenPing方法,排程器可以在任務完成之前和之後自動ping給定的URL。該方法在通知外部服務時很有用,例如Laravel Envoyer,在排程任務開始或完成的時候:

$schedule->command('emails:send')
         ->daily()
         ->pingBefore($url)
         ->thenPing($url);

使用pingBefore($url)thenPing($url)特性需要安裝HTTP庫Guzzle,可以在composer.json 檔案中新增如下行來安裝Guzzle到專案:

"guzzlehttp/guzzle": "~5.3|~6.0"

每一天都要進步一點點!

相關文章