# Laravel 的任務調度(計劃任務)功能 Task Scheduling
- [簡介](#introduction)
- [定義調度](#defining-schedules)
- [調度頻率設置](#schedule-frequency-options)
- [避免任務重復](#preventing-task-overlaps)
- [維護模式](#maintenance-mode)
- [任務輸出](#task-output)
- [任務鉤子](#task-hooks)
<a name="introduction"></a>
## 簡介
在過去,開發者必須為每個需要調度的任務生成單獨的 Cron 項目。然而令人頭疼的是任務調度不受版本控制,并且需要 SSH 到服務器上來增加 Cron 條目。
Laravel 命令調度器允許你在 Laravel 中對命令調度進行清晰流暢的定義,并且僅需要在服務器上增加一條 Cron 項目即可。你的調度已經定義在 `app/Console/Kernel.php` 文件的 `schedule` 方法中。為了方便你開始,在該方法內包含了一個簡單的例子。你可以隨意增加調度到 `Schedule` 對象中。
### 啟動調度器
使用調度器時,你只需要把 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 會評估你的計劃任務并運行預定任務。
<a name="defining-schedules"></a>
## 定義調度
你可以將所有的計劃任務定義在 `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 = [
\App\Console\Commands\Inspire::class,
];
/**
* 定義應用的命令調度
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
DB::table('recent_users')->delete();
})->daily();
}
}
除了計劃 `Closure` 調用,你還能計劃 [Artisan 命令](/docs/{{version}}/artisan) 以及系統命令操作。舉個例子,你可以使用 `command` 方法傳參命令名稱或者命令類名稱來計劃一個 `Artisan` 命令:
$schedule->command('emails:send --force')->daily();
$schedule->command(EmailsCommand::class, ['--force'])->daily();
`exec` 命令可發送命令到操作系統上:
$schedule->exec('node /home/forge/script.js')->daily();
<a name="schedule-frequency-options"></a>
### 調度頻率設置
當然,你可以針對你的任務來分配多種調度計劃:
| 方法 | 描述 |
| --------------------------------- | ------------------------- |
| `->cron('* * * * * *');` | 自定義調度任務 |
| `->everyMinute();` | 每分鐘執行一次任務 |
| `->everyFiveMinutes();` | 每五分鐘執行一次任務 |
| `->everyTenMinutes();` | 每十分鐘執行一次任務 |
| `->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` 時才運行。
<a name="preventing-task-overlaps"></a>
### 避免任務重復
默認情況,即便之前相同的任務主體仍未結束,現有計劃任務依舊會被運行。為了避免這個問題,你可以使用 `withoutOverlapping` 方法:
$schedule->command('emails:send')->withoutOverlapping();
在這個例子中,如果沒有其它 `emails:send` [Artisan 命令](/docs/{{version}}/artisan) 在運行的話,此任務將于每分鐘被運行一次。當你有些任務運行時間過長,且無法預測出具體所需時間時,`withoutOverlapping` 方法將會特別有幫助。
<a name="maintenance-mode"></a>
### 維護模式
當應用進入 [維護模式](/docs/{{version}}/configuration#maintenance-mode) 時,默認情況下 Laravel 的調度功能將會停止運行。這是因為我們考慮到你可能在維護模式下做一些破壞性的維護工作,我們不想讓任務調度對這些工作造成干擾。然而,如果你想強制某個任務在維護模式下運行的話,你可以使用 `evenInMaintenanceMode` 方法:
$schedule->command('emails:send')->evenInMaintenanceMode();
<a name="task-output"></a>
## 任務輸出
Laravel 調度器為任務調度輸出提供多種便捷方法。首先,通過 `sendOutputTo` 方法你可以發送輸出到單個文件上以便后續檢查:
$schedule->command('emails:send')
->daily()
->sendOutputTo($filePath);
如果想將輸出附加到指定的文件上,則可以使用 `appendOutputTo` 方法:
$schedule->command('emails:send')
->daily()
->appendOutputTo($filePath);
通過 `emailOutputTo` 方法,你可以發送輸出到你所指定的電子郵件上。注意,你必須先通過 `sendOutputTo` 方法將其輸出到一個文件。同時,在郵件發出之前,你需要先設置 Laravel 的 [電子郵件服務](/docs/{{version}}/mail):
$schedule->command('foo')
->daily()
->sendOutputTo($filePath)
->emailOutputTo('foo@example.com');
> {note} `emailOutputTo`, `sendOutputTo` 和 `appendOutputTo` 方法只適用于 `command` 方法,不支持 `call` 方法。
<a name="task-hooks"></a>
## 任務鉤子
通過 `before` 與 `after` 方法,你能讓特定的代碼在任務完成之前及之后運行:
$schedule->command('emails:send')
->daily()
->before(function () {
// 任務就要開始了…
})
->after(function () {
// 任務完成…
});
#### Ping 網址
通過 `pingBefore` 與 `thenPing` 方法,調度器能自動的在一個任務完成之前或之后 ping 一個指定的網址。該方法在你計劃的任務進行或完成時,可用來有效的通知一個外部服務,例如 [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
## 譯者署名
| 用戶名 | 頭像 | 職能 | 簽名 |
| ---------------------------------------- | ---------------------------------------- | ---- | ---------------------------------------- |
| [@沈益飛](https://laravel-china.org/users/13655) | <img class="avatar-66 rm-style" src="https://dn-phphub.qbox.me/uploads/avatars/13655_1490162781.png?imageView2/1/w/100/h/100"> | 翻譯 | [@m809745357](https://github.com/m809745357) at Github |
---
> {note} 歡迎任何形式的轉載,但請務必注明出處,尊重他人勞動共創開源社區。
>
> 轉載請注明:本文檔由 Laravel China 社區 [laravel-china.org](https://laravel-china.org) 組織翻譯,詳見 [翻譯召集帖](https://laravel-china.org/topics/5756/laravel-55-document-translation-call-come-and-join-the-translation)。
>
> 文檔永久地址: https://d.laravel-china.org
- 說明
- 翻譯說明
- 發行說明
- 升級說明
- 貢獻導引
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- HomeStead
- Valet
- 核心架構
- 請求周期
- 服務容器
- 服務提供者
- 門面(Facades)
- Contracts
- 基礎功能
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- 重定向
- Session
- 表單驗證
- 錯誤與日志
- 前端開發
- Blade 模板
- 本地化
- 前端指南
- 編輯資源 Mix
- 安全
- 用戶認證
- API認證
- 用戶授權
- 加密解密
- 哈希
- 重置密碼
- 綜合話題
- Artisan 命令行
- 廣播系統
- 緩存系統
- 集合
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent 集合
- 修改器
- API 資源
- 序列化
- 測試
- 快速入門
- HTTP 測試
- 瀏覽器測試 Dusk
- 數據庫測試
- 測試模擬器
- 官方擴展包
- Cashier 交易工具包
- Envoy 部署工具
- Horizon
- Passport OAuth 認證
- Scout 全文搜索
- Socialite 社交化登錄
- 交流說明