任務調度


###############################################' ###########時間範圍限制############閉包測試限制############環境約束####### #########時區############避免任務重複############任務只執行在一台伺服器上##### #######後台任務############維護模式################任務輸出########## ##任務鉤子###############Ping 網址###########################

任務排程

任務鉤子

#簡介 過去,你可能需要在伺服器上為每一個調度任務去建立Cron 入口。但這種方式很快就會變得不友好,因為這些任務調度不在原始程式碼中,而且你每次都需要透過 SSH 連結登入伺服器中才能增加 Cron 入口。

Laravel 命令列調度器允許你在 Laravel 中對命令調度進行清晰流暢的定義。而使用這個任務調度器時,你只需要在你的伺服器上建立單一 Cron 入口介面。你的任務排程在
app/Console/Kernel.php

schedule

方法中進行定義。為了幫助你更好的入門,這個方法有一個簡單的例子。

啟動調度器當使用這個調度器時,你只需要把下面的 Cron 入口加入到你的伺服器中即可。如果你不知道怎麼在伺服器中加入Cron 入口,可以考慮使用一些服務來管理Cron 入口,例如

Laravel Forge

* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
這個Cron 為每分鐘執行一次Laravel 的命令列調度器。當

schedule:run
指令被執行的時候,Laravel 會根據你的調度執行預定的程式。

定義調度

你可以在App\Console\Kernel 類別的

schedule

方法中定義所有的排程任務。在開始之前,讓我們來看一個例子。在這個例子中,我們計劃每天午夜呼叫一個閉包。在閉包中,我們執行一個資料庫查詢來清空一張表:

<?php
    namespace App\Console;
    use Illuminate\Support\Facades\DB;
    use Illuminate\Console\Scheduling\Schedule;
    use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
    class Kernel extends ConsoleKernel{    
    /**
     * 应用里的自定义 Artisan 命令
     *
     * @var array
     */   
     protected $commands = [    
         //   
       ];   
    /**
     * 定义计划任务
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */  
    protected function schedule(Schedule $schedule) 
       {     
          $schedule->call(function () {       
          DB::table('recent_users')->delete();      
        })->daily();  
      }
   }
除了使用閉包來定義任務調度外,你也可以用

invokable objects
$schedule->call(new DeleteRecentUsers)->daily();
###################

Artisan 指令調度

除了使用呼叫閉包這種方式來調度外,你還可以呼叫  Artisan 指令 和作業系統指令。例如,你可以給 command 方法傳遞指令名稱或類別來調度一個 Artisan 指令。

$schedule->command('emails:send --force')->daily();
$schedule->command(EmailsCommand::class, ['--force'])->daily();

佇列任務排程

job 方法可以用來調度 佇列任務。此方法提供了一種快速的方式來調度任務,而無需使用 call 方法建立閉包來調度任務。

$schedule->job(new Heartbeat)->everyFiveMinutes();
// 分发任务到「heartbeats」队列...
$schedule->job(new Heartbeat, 'heartbeats')->everyFiveMinutes();

Shell 命令調度

exec 方法可用來向作業系統發送命令:

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

#

調度頻率設定

當然了,你可以為你的任務分配多種排程:

##->everyTenMinutes();##->everyFifteenMinutes ();->everyThirtyMinutes();->hourly();#->hourlyAt(17);##每小時第17 分鐘執行一次任務##-> ;daily();每天午夜執行一次任務(譯者註:每天零點)->dailyAt('13: 00');每天13 點執行一次任務###############->twiceDaily(1, 13);####### ###每天1 點和13 點分別執行一次任務###############->weekly();#########每週執行一次任務# ##############->weeklyOn(1, '8:00');#########每週一的8 點執行一次任務##### #每季執行一次任務每年執行一次任務設定時區

結合其他一些特定條件,我們可以產生在一週中特定時間運行的任務。舉個例子,在每週一執行指令:

// 每周一 13:00 执行...
$schedule->call(function () { 
   //
   })->weekly()->mondays()->at('13:00');
 // 工作日(周一至周五) 8点 至 17 点每小时执行一次...
$schedule->command('foo')        
  ->weekdays()          
  ->hourly()          
  ->timezone('America/Chicago')          
  ->between('8:00', '17:00');

額外的限制條件清單如下:

方法描述
#->cron('* * * * *');自訂Cron 時間表執行任務
#->everyMinute();#每分鐘執行一次任務
->everyFiveMinutes();每五分鐘執行一次任務
#每十分鐘執行一次任務
每十五分鐘執行一次任務
#每三十分鐘執行一次任務
每小時執行一次任務
->monthly();每月執行一次任務
->monthlyOn( 4, '15:00');每月4 號的15 點執行一次任務
##->quarterly();
->yearly();
->timezone('America/New_York');
限制任務在星期三執行限制任務在星期四執行->fridays();->saturdays();-> between($start, $end);$start$end 之間執行
方法描述
->weekdays();限制任務在工作日執行
->weekends();限制任務在週末執行
->sundays();限制任務在星期日執行
->mondays();限制任務在週一執行
->tuesdays();#限制任務在星期二執行
## ->wednesdays();
->thursdays();
限制任務在星期五執行
限制任務在星期六執行
限制任務在
### ######->when(Closure);#########當閉包回傳為真時執行###############->environments ($env);#########限制任務在特定環境中執行############

時間範圍限制

使用between 來限制任務在一天中的某個時間段來執行:

$schedule->command('reminders:send')     
               ->hourly()                    
               ->between('7:00', '22:00');

或使用unlessBetween 方法來為任務排除一個時間段:

$schedule->command('reminders:send')              
      ->hourly()                    
      ->unlessBetween('23:00', '4:00');

#閉包測試限制

使用when 方法來根據測試結果來執行任務。也就是說,如果給定的閉包回傳結果為true,只要沒有其他約束條件阻止任務運行,任務就會一直執行下去:

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

skip 方法可以看做是when 方法的逆過程。如果skip 方法回傳true,任務就不會執行:

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

使用鍊式呼叫when 方法時,只有所有的when 都回傳true 時,任務才會執行。

環境約束

environments 方法可用於僅在給定環境中執行任務:

$schedule->command('emails:send')         
   ->daily()            
   ->environments(['staging', 'production']);

#時區

使用timezone 方法,你可以指定任務在給定的時區內執行:

$schedule->command('report:generate')      
   ->timezone('America/New_York')        
   ->at('02:00')

如果要為所有計劃任務分配相同的時區,則可能希望在app/Console/Kernel.php 檔案中定義scheduleTimezone 方法。此方法應傳回應指派給所有排程任務的預設時區:

/**
 * 获取默认情况下应为预定事件使用的时区。
 *
 * @return \DateTimeZone|string|null
 */
 protected function scheduleTimezone(){  
   return 'America/Chicago';
 }

{註} 請記住,有些時區會使用夏令時間。當夏時制時間改變時,你的任務可能會執行兩次,甚至根本不會執行。所以我們建議盡量避免使用時區來安排計畫任務。

避免任務重複

預設情況下,即使先前的任務還在執行,調度內任務也會執行。你可以使用withoutOverlapping 方法來避免這種情況:

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

在這個範例中,如果emails:send Artisan 指令沒有正在運行,它將每分鐘執行一次。如果你的任務執行時間不確定,而且你又無法準確預估出任務的執行時間,那麼  withoutOverlapping 方法會顯得特別有用。

如果有需要,你可以指定「without overlapping」鎖定指定的時間範圍。預設情況下,鎖將在 24 小時後過期。

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

#

任務只運行在一台伺服器上

{note} 要使用這個特性,你的應用程式預設快取驅動必須是memcachedredis。除此之外,所有的伺服器必須使用同一個中央快取伺服器通訊。

如果你的應用程式在多個伺服器上運行,你可能需要限制你的調度任務只在單一伺服器上運行。假設你有一個調度任務:每週五晚產生一份新報告。如果這個任務調度器在三個伺服器上運行,那麼這個任務會在三台伺服器上運行且產生三份報告。這樣不好!

為了說明任務應該在單一伺服器上執行,在定義排程任務時使用 onOneServer 方法。第一個取得到任務的伺服器會產生一個原子鎖,用來防止其他伺服器在同一時刻執行相同任務。

$schedule->command('report:generate')             
   ->fridays()                
   ->at('17:00')                
   ->onOneServer();

後台任務

預設情況下,同時調度的多個命令會依序執行。如果你有長時間運行的命令,這可能會導致後續命令的啟動時間比預期的要晚。因此,你想要在後台同時執行指令,可以使用runInBackground 方法:

$schedule->command('analytics:report')      
   ->daily()         
   ->runInBackground();

##維護模式

Laravel 的佇列任務在維護模式下不會運作。因為我們不想你的調度任務幹擾到你伺服器上可能還未完成的專案。不過,如果你確實是想在維護模式下強制調度任務執行,你可以使用

evenInMaintenanceMode 方法:

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

任務輸出

Laravel 調度器提供了一些方便的方法來處理調度任務輸出。首先,你可以使用sendOutputTo

方法來輸出到文件以便於後續檢查:
$schedule->command('emails:send')    
     ->daily()         
     ->sendOutputTo($filePath);
如果希望將輸出

附加

到給定文件,可以使用

appendOutputTo 方法

$schedule->command('emails:send')     
    ->daily()         
    ->appendOutputTo($filePath);
使用emailOutputTo

# 方法,你可以將輸出傳送到指定郵箱。在使用郵件發送之前,你需要設定Laravel 的郵件服務:
$schedule->command('foo')   
      ->daily()         
      ->sendOutputTo($filePath)        
      ->emailOutputTo('foo@example.com');
###{note} ###emailOutputTo###,###sendOutputTo### 和###appendOutputTo####方法是 ###command### 和###exec### 獨有的。 ###########################任務鉤子######使用###before### 和###after## # 方法,你可以在調度任務執行前或執行後來執行特定程式碼:###
$schedule->command('emails:send')   
      ->daily()         
      ->before(function () {         
          // Task is about to start...      
            })      
      ->after(function () {       
           // Task is complete...      
         });
#######

Ping 網址

使用 pingBeforethenPing 方法,你可以在任務執行前或執行後來 ping 指定的 URL。這個方法在通知外部服務(例如Laravel Envoyer)時將會特別有用:

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

只有在給定條件為true 時,才能使用 pingBeforeIfthenPingIf 方法ping 指定的URL:

$schedule->command('emails:send')      
   ->daily()         
   ->pingBeforeIf($condition, $url)         
   ->thenPingIf($condition, $url);

所有ping 方法都需要Guzzle HTTP 函式庫。你可以使用 Composer 來新增 Guzzle 到你的專案中:

composer require guzzlehttp/guzzle
這篇文章首發在 LearnKu.com 網站上。