* * * * *
[TOC]
## 簡介
在過去,開發者必須在服務器上為每個任務生成單獨的 Cron 項目。而令人頭疼的是任務調度不受源代碼控制,而且必須通過 SSH 連接到服務器上來增加 Cron 項目。
Laravel 的命令調度程序允許你在 Laravel 中對命令調度進行清晰流暢的定義。并且在使用調度程序時,只需要在服務器上增加一條 Cron 項目即可。調度是在?`app/Console/Kernel.php`?文件的?`schedule`?方法中定義的。為了方便你開始,該方法已經包含了一個簡單的例子。
### 啟動調度器
使用調度器時,只需將以下 Cron 項目添加到服務器。如果你不知道如何將 Cron 項目添加到服務器,可以考慮使用?[Laravel Forge](https://forge.laravel.com/)?等服務來管理你的 Cron 項目:
~~~
* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
~~~
上面這個 Cron 會每分鐘調用一次 Laravel 命令調度器。執行?`schedule:run`?命令時, Laravel 會根據你的調度運行預定任務。
## 定義調度
你可以在?`App\Console\Kernel`?類的?`schedule`?方法中定義所有調度任務。在開始之前,先看看一個調度任務的例子。在該例子中,我們將計劃在每天午夜調用一個?`Closure`。在這個?`Closure`?中,將執行一個數據庫查詢來清除一個表:
~~~
<?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 = [
//
];
/**
*定義應用的命令調度
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
DB::table('recent_users')->delete();
})->daily();
}
}
~~~
### Artisan 命令調度
除了通過以?`Closure calls`?這種方式調度,你還能調度?[Artisan 命令](http://www.hmoore.net/tonyyu/laravel_5_6/786238)?以及系統命令。舉個例子,你可以給?`command`方法傳遞命令名稱或者類名稱來調度一個?`Artisan`?命令:
~~~
$schedule->command('emails:send --force')->daily();
$schedule->command(EmailsCommand::class, ['--force'])->daily();
~~~
### 隊列任務調度
`job`?方法可以用來調度[隊列任務](http://www.hmoore.net/tonyyu/laravel_5_6/786248)。這個方法提供了一種快捷方式來調度任務,而無需使用?`call`?方法手動創建閉包來調度任務:
~~~
$schedule->job(new Heartbeat)->everyFiveMinutes();
~~~
### Shell 命令調度
`exec`?方法可用于向操作系統發出命令:
~~~
$schedule->exec('node /home/forge/script.js')->daily();
~~~
### 調度頻率設置
當然,你可以為你的任務分配多種調度計劃:
| 方法 | 描述 |
| --- | --- |
| `->cron('* * * * *');` | 在自定義的 Cron 時間表上執行該任務 |
| `->everyMinute();` | 每分鐘執行一次任務 |
| `->everyFiveMinutes();` | 每五分鐘執行一次任務 |
| `->everyTenMinutes();` | 每十分鐘執行一次任務 |
| `->everyFifteenMinutes();` | 每十五分鐘執行一次任務 |
| `->everyThirtyMinutes();` | 每半小時執行一次任務 |
| `->hourly();` | 每小時執行一次任務 |
| `->hourlyAt(17);` | 每小時的第 17 分鐘執行一次任務 |
| `->daily();` | 每天午夜執行一次任務 |
| `->dailyAt('13:00');` | 每天的 13:00 執行一次任務 |
| `->twiceDaily(1, 13);` | 每天的 1:00 和 13:00 分別執行一次任務 |
| `->weekly();` | 每周執行一次任務 |
| `->monthly();` | 每月執行一次任務 |
| `->monthlyOn(4, '15:00');` | 在每個月的第四天的 15:00 執行一次任務 |
| `->quarterly();` | 每季度執行一次任務 |
| `->yearly();` | 每年執行一次任務 |
| `->timezone('America/New_York');` | 設置時區 |
這些方法可以合并其它限制條件以生成更精確的調度。例如,計劃每周周一的調度:
~~~
// 每周一的下午一點鐘運行
$schedule->call(function () {
//
})->weekly()->mondays()->at('13:00');
// 周一至周五上午 8 點至下午 5 點每小時運行一次...
$schedule->command('foo')
->weekdays()
->hourly()
->timezone('America/Chicago')
->between('8:00', '17:00');
~~~
以下是額外限制條件的列表:
| 方法 | 描述 |
| --- | --- |
| `->weekdays();` | 將任務限制在工作日 |
| `->sundays();` | 將任務限制在星期天 |
| `->mondays();` | 將任務限制在星期一 |
| `->tuesdays();` | 將任務限制在星期二 |
| `->wednesdays();` | 將任務限制在星期三 |
| `->thursdays();` | 將任務限制在星期四 |
| `->fridays();` | 將任務限制在星期五 |
| `->saturdays();` | 將任務限制在星期六 |
| `->between($start, $end);` | 限制任務運行在開始到結束時間范圍內 |
| `->when(Closure);` | 根據閉包函數的返回來限制任務 |
#### 時間范圍限制
`between`?方法可以用來限制一天中某個時間范圍內的任務執行:
~~~
$schedule->command('reminders:send')
->hourly()
->between('7:00', '22:00');
~~~
類似的,?`unlessBetween`?方法可以用來在一段時間內排除任務的執行:
~~~
$schedule->command('reminders:send')
->hourly()
->unlessBetween('23:00', '4:00');
~~~
#### 閉包測試限制
`when`?方法可以用來根據給定的測試的結果來限制任務的執行。換句話說,如果給定的?`Closure`?返回?`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`?時才運行。
### 時區
使用?`timezone`?方法,您可以指定計劃任務的時間應該在給定時區內執行:
~~~
$schedule->command('report:generate')
->timezone('America/New_York')
->at('02:00')
~~~
> {note} 請記住,有些時區會利用夏令時。 夏時制時間發生變化時,您的計劃任務可能會運行兩次,甚至根本不運行。 出于這個原因,我們建議盡可能避免使用時區來安排計劃任務。
### 避免任務重復
默認情況,即便有相同的任務還在運行,調度內的任務依舊會被執行。為了避免這個問題,你可以使用?`withoutOverlapping`?方法:
~~~
$schedule->command('emails:send')->withoutOverlapping();
~~~
在上面這個例子中,如果沒有其它?[Artisan 命令](http://www.hmoore.net/tonyyu/laravel_5_6/786238)?`emails:send`?在運行的話,此任務將于每分鐘被運行一次。如果你的任務在執行時間上有很大的不同,你無法準確預測給定任務需要多長時間,?`withoutOverlapping`?方法將會特別有幫助。
如果需要, 你可以指定在 "without overlapping" 鎖過期之前必須經過多少分鐘。 默認情況下,鎖將在24小時后過期:
~~~
$schedule->command('emails:send')->withoutOverlapping(10);
~~~
### 任務只運行在一臺服務器上
> {note} 要使用這個功能,必須使用?`memcached`?或?`redis`?作為你應用程序的默認緩存驅動程序。 另外,所有服務器都必須與同一個中央緩存服務器進行通信。
如果您的應用程序在多個服務器上運行,則你可能會想限制計劃任務僅在單個服務器上執行。 例如,您有一個計劃任務,每個星期五晚上都會生成一個新報告。 如果在三臺服務器上運行,則將在所有三臺服務器上運行并生成三次報告。 這樣不好!
為了讓計劃任務只在一臺服務器上運行,在定義計劃任務時可使用?`onOneServer`?方法。 原理是獲取到該任務的第一臺服務器將對該任務加上原子鎖,以防止其他服務器同時運行相同的任務:
~~~
$schedule->command('report:generate')
->fridays()
->at('17:00')
->onOneServer();
~~~
### 維護模式
當 Laravel 處于?[維護模式](http://www.hmoore.net/tonyyu/laravel_5_6/786051#_72)時,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 的?[e-mail services](http://www.hmoore.net/tonyyu/laravel_5_6/786245):
~~~
$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 () {
// 任務開始...
})
->after(function () {
// 任務結束...
});
~~~
#### Ping 網址
使用?`pingBefore`?與?`thenPing`?方法,調度器可以在任務完成之前或之后自動 ping 給定的 URL。這個方法對于通知外部服務(例如?[Laravel Envoyer](https://envoyer.io/))你的調度任務正在開始或已經完成執行很有用:
~~~
$schedule->command('emails:send')
->daily()
->pingBefore($url)
->thenPing($url);
~~~
無論是使用?`pingBefore($url)`?還是?`thenPing($url)`?的功能,都需要 Guzzle HTTP 函數庫的支持。你可以使用 Composer 將 Guzzle 函數庫添加到你的項目中:
~~~
composer require guzzlehttp/guzzle
~~~
> 譯者注:使用?`runInBackground`?方法可使當前計劃任務進入后臺運行,從而不阻塞其他任務的執行。
- 前言
- 翻譯說明
- 發行說明
- 升級指南
- 貢獻導引
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- Homestead
- Valet
- 部署
- 核心架構
- 請求周期
- 服務容器
- 服務提供者
- Facades
- Contracts
- 基礎功能
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- URL
- Session
- 表單驗證
- 錯誤
- 日志
- 前端開發
- Blade 模板
- 本地化
- 前端指南
- 編輯資源 Mix
- 安全相關
- 用戶認證
- Passport OAuth 認證
- 用戶授權
- 加密解密
- 哈希
- 重置密碼
- 綜合話題
- Artisan 命令行
- 廣播系統
- 緩存系統
- 集合
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent 集合
- 修改器
- API 資源
- 序列化
- 測試相關
- 快速入門
- HTTP 測試
- 瀏覽器測試 Dusk
- 數據庫測試
- 測試模擬器
- 官方擴展包
- Cashier 交易工具包
- Envoy 部署工具
- Horizon
- Scout 全文搜索
- Socialite 社會化登錄