Laravel: Lập lịch tác vụ
Giới thiệu
Trước đây, bạn có thể đã viết một mục cấu hình cron cho mỗi tác vụ bạn cần lập lịch trên máy chủ của mình. Tuy nhiên, điều này có thể nhanh chóng trở thành vấn đề vì lịch trình tác vụ của bạn không còn trong quyền kiểm soát nguồn và bạn phải SSH vào máy chủ của mình để xem các mục cron hiện có hoặc thêm các mục bổ sung.
Bộ lập lịch lệnh của Laravel cung cấp một cách tiếp cận mới để quản lý các tác vụ đã lên lịch trên máy chủ của bạn. Bộ lập lịch cho phép bạn xác định lịch biểu lệnh một cách trôi chảy và rõ ràng trong chính ứng dụng Laravel của bạn. Khi sử dụng bộ lập lịch, chỉ cần một mục nhập cron duy nhất trên máy chủ của bạn. Lịch trình tác vụ của bạn được xác định trong phương thức app/Console/Kernel.php
của tệp schedule
. Để giúp bạn bắt đầu, một ví dụ đơn giản được xác định trong phương thức.
Xác định lịch biểu
Bạn có thể xác định tất cả các tác vụ đã lên lịch của mình trong schedule
phương thức của App\Console\Kernel
lớp ứng dụng của bạn . Để bắt đầu, chúng ta hãy xem một ví dụ. Trong ví dụ này, chúng tôi sẽ lên lịch đóng cửa hàng ngày vào lúc nửa đêm. Trong phần đóng, chúng tôi sẽ thực hiện một truy vấn cơ sở dữ liệu để xóa một bảng:
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\DB;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
DB::table('recent_users')->delete();
})->daily();
}
}
Ngoài việc lập lịch sử dụng các bao đóng, bạn cũng có thể lập lịch cho các đối tượng bất khả xâm phạm . Các đối tượng có thể xâm nhập là các lớp PHP đơn giản có chứa một __invoke
phương thức:
$schedule->call(new DeleteRecentUsers)->daily();
Nếu bạn muốn xem tổng quan về các tác vụ đã lên lịch của mình và vào lần tiếp theo chúng được lên lịch chạy, bạn có thể sử dụng schedule:list
lệnh Artisan:
php artisan schedule:list
Lập lịch Lệnh cho Nghệ nhân
Ngoài việc lên lịch đóng cửa, bạn cũng có thể lên lịch cho lệnh Artisan và lệnh hệ thống. Ví dụ, bạn có thể sử dụng command
phương thức để lập lịch một lệnh Artisan bằng cách sử dụng tên hoặc lớp của lệnh.
Khi lên lịch cho các lệnh Artisan bằng cách sử dụng tên lớp của lệnh, bạn có thể chuyển một mảng các đối số dòng lệnh bổ sung sẽ được cung cấp cho lệnh khi nó được gọi:
use App\Console\Commands\SendEmailsCommand;
$schedule->command('emails:send Taylor --force')->daily();
$schedule->command(SendEmailsCommand::class, ['Taylor', '--force'])->daily();
Lên lịch công việc đã xếp hàng
Các job
phương pháp có thể được sử dụng để sắp xếp một công việc xếp hàng đợi . Phương thức này cung cấp một cách thuận tiện để lập lịch các công việc đã được xếp hàng mà không cần sử dụng call
phương thức để xác định các bao đóng để xếp hàng công việc:
use App\Jobs\Heartbeat;
$schedule->job(new Heartbeat)->everyFiveMinutes();
Đối số thứ hai và thứ ba tùy chọn có thể được cung cấp cho job
phương thức chỉ định tên hàng đợi và kết nối hàng đợi sẽ được sử dụng để xếp hàng công việc:
use App\Jobs\Heartbeat;
// Dispatch the job to the "heartbeats" queue on the "sqs" connection...
$schedule->job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();
Lập lịch các lệnh Shell
Các exec
phương pháp có thể được sử dụng để ra lệnh cho hệ điều hành:
$schedule->exec('node /home/forge/script.js')->daily();
Lên lịch tùy chọn tần suất
Chúng tôi đã thấy một vài ví dụ về cách bạn có thể định cấu hình một tác vụ để chạy trong các khoảng thời gian được chỉ định. Tuy nhiên, có nhiều tần suất lịch trình tác vụ khác mà bạn có thể chỉ định cho một tác vụ:
Phương pháp | Sự miêu tả |
---|---|
->cron('* * * * *'); |
Chạy tác vụ theo lịch trình cron tùy chỉnh |
->everyMinute(); |
Chạy nhiệm vụ mỗi phút |
->everyTwoMinutes(); |
Chạy tác vụ hai phút một lần |
->everyThreeMinutes(); |
Chạy nhiệm vụ ba phút một lần |
->everyFourMinutes(); |
Chạy nhiệm vụ bốn phút một lần |
->everyFiveMinutes(); |
Chạy tác vụ năm phút một lần |
->everyTenMinutes(); |
Chạy nhiệm vụ mười phút một lần |
->everyFifteenMinutes(); |
Chạy tác vụ cứ sau mười lăm phút một lần |
->everyThirtyMinutes(); |
Chạy tác vụ ba mươi phút một lần |
->hourly(); |
Chạy nhiệm vụ mỗi giờ |
->hourlyAt(17); |
Chạy nhiệm vụ mỗi giờ vào lúc 17 phút trước giờ |
->everyTwoHours(); |
Chạy nhiệm vụ hai giờ một lần |
->everyThreeHours(); |
Chạy nhiệm vụ ba giờ một lần |
->everyFourHours(); |
Chạy nhiệm vụ bốn giờ một lần |
->everySixHours(); |
Chạy nhiệm vụ sáu giờ một lần |
->daily(); |
Chạy nhiệm vụ hàng ngày vào lúc nửa đêm |
->dailyAt('13:00'); |
Chạy nhiệm vụ hàng ngày lúc 13:00 |
->twiceDaily(1, 13); |
Chạy nhiệm vụ hàng ngày lúc 1:00 & 13:00 |
->weekly(); |
Chạy nhiệm vụ vào Chủ nhật hàng tuần lúc 00:00 |
->weeklyOn(1, '8:00'); |
Chạy nhiệm vụ hàng tuần vào Thứ Hai lúc 8:00 |
->monthly(); |
Chạy nhiệm vụ vào ngày đầu tiên hàng tháng lúc 00:00 |
->monthlyOn(4, '15:00'); |
Chạy nhiệm vụ hàng tháng vào ngày 4 lúc 15:00 |
->twiceMonthly(1, 16, '13:00'); |
Chạy nhiệm vụ hàng tháng vào ngày 1 và 16 lúc 13:00 |
->lastDayOfMonth('15:00'); |
Chạy nhiệm vụ vào ngày cuối cùng của tháng lúc 15:00 |
->quarterly(); |
Chạy nhiệm vụ vào ngày đầu tiên của mỗi quý lúc 00:00 |
->yearly(); |
Chạy nhiệm vụ vào ngày đầu tiên hàng năm lúc 00:00 |
->yearlyOn(6, 1, '17:00'); |
Chạy nhiệm vụ hàng năm vào ngày 1 tháng 6 lúc 17:00 |
->timezone('America/New_York'); |
Đặt múi giờ cho nhiệm vụ |
Các phương pháp này có thể được kết hợp với các ràng buộc bổ sung để tạo ra các lịch biểu được điều chỉnh tinh vi hơn chỉ chạy vào một số ngày nhất định trong tuần. Ví dụ: bạn có thể lập lịch chạy lệnh hàng tuần vào Thứ Hai:
// Run once per week on Monday at 1 PM...
$schedule->call(function () {
//
})->weekly()->mondays()->at('13:00');
// Run hourly from 8 AM to 5 PM on weekdays...
$schedule->command('foo')
->weekdays()
->hourly()
->timezone('America/Chicago')
->between('8:00', '17:00');
Dưới đây là danh sách các ràng buộc lịch trình bổ sung:
Phương pháp | Sự miêu tả |
---|---|
->weekdays(); |
Giới hạn nhiệm vụ trong các ngày trong tuần |
->weekends(); |
Giới hạn nhiệm vụ vào cuối tuần |
->sundays(); |
Giới hạn nhiệm vụ đến Chủ nhật |
->mondays(); |
Giới hạn nhiệm vụ đến Thứ Hai |
->tuesdays(); |
Giới hạn nhiệm vụ đến Thứ Ba |
->wednesdays(); |
Giới hạn nhiệm vụ đến Thứ Tư |
->thursdays(); |
Giới hạn nhiệm vụ đến Thứ Năm |
->fridays(); |
Giới hạn nhiệm vụ đến Thứ Sáu |
->saturdays(); |
Giới hạn nhiệm vụ đến Thứ Bảy |
->days(array|mixed); |
Giới hạn nhiệm vụ trong những ngày cụ thể |
->between($startTime, $endTime); |
Giới hạn nhiệm vụ phải chạy giữa thời gian bắt đầu và kết thúc |
->unlessBetween($startTime, $endTime); |
Giới hạn nhiệm vụ để không chạy giữa thời gian bắt đầu và kết thúc |
->when(Closure); |
Giới hạn nhiệm vụ dựa trên một bài kiểm tra sự thật |
->environments($env); |
Giới hạn nhiệm vụ trong các môi trường cụ thể |
Ràng buộc trong ngày
Các days
phương pháp có thể được sử dụng để hạn chế việc thực hiện một nhiệm vụ để ngày cụ thể trong tuần. Ví dụ: bạn có thể lập lịch để lệnh chạy hàng giờ vào Chủ Nhật và Thứ Tư:
$schedule->command('emails:send')
->hourly()
->days([0, 3]);
Ngoài ra, bạn có thể sử dụng các hằng số có sẵn trên Illuminate\Console\Scheduling\Schedule
lớp khi xác định ngày mà một tác vụ sẽ chạy:
use Illuminate\Console\Scheduling\Schedule;
$schedule->command('emails:send')
->hourly()
->days([Schedule::SUNDAY, Schedule::WEDNESDAY]);
Ràng buộc giữa thời gian
Các between
phương pháp có thể được sử dụng để hạn chế việc thực hiện một nhiệm vụ căn cứ vào thời gian trong ngày:
$schedule->command('emails:send')
->hourly()
->between('7:00', '22:00');
Tương tự, unlessBetween
phương pháp này có thể được sử dụng để loại trừ việc thực thi một tác vụ trong một khoảng thời gian:
$schedule->command('emails:send')
->hourly()
->unlessBetween('23:00', '4:00');
Ràng buộc Kiểm tra Sự thật
Các when
phương pháp có thể được sử dụng để hạn chế việc thực hiện một nhiệm vụ dựa trên kết quả của một thử nghiệm thật nhất định. Nói cách khác, nếu bao đóng đã cho trả về true
, tác vụ sẽ thực thi miễn là không có điều kiện ràng buộc nào khác ngăn tác vụ chạy:
$schedule->command('emails:send')->daily()->when(function () {
return true;
});
Các skip
phương pháp có thể được xem như là nghịch đảo của when
. Nếu skip
phương thức trả về true
, tác vụ đã lên lịch sẽ không được thực thi:
$schedule->command('emails:send')->daily()->skip(function () {
return true;
});
Khi sử dụng when
các phương thức chuỗi , lệnh đã lên lịch sẽ chỉ thực thi nếu tất cả các when
điều kiện trả về true
.
Các ràng buộc về môi trường
Các environments
phương pháp có thể được sử dụng để thực hiện nhiệm vụ duy nhất trên môi trường nhất định (theo quy định của APP_ENV
biến môi trường ):
$schedule->command('emails:send')
->daily()
->environments(['staging', 'production']);
Múi giờ
Sử dụng timezone
phương pháp này, bạn có thể chỉ định rằng thời gian của nhiệm vụ đã lên lịch sẽ được diễn giải trong một múi giờ nhất định:
$schedule->command('report:generate')
->timezone('America/New_York')
->at('2:00')
Nếu bạn liên tục chỉ định cùng một múi giờ cho tất cả các tác vụ đã lên lịch của mình, bạn có thể muốn xác định một scheduleTimezone
phương thức trong App\Console\Kernel
lớp của mình . Phương thức này sẽ trả về múi giờ mặc định sẽ được chỉ định cho tất cả các tác vụ đã lên lịch:
/**
* Get the timezone that should be used by default for scheduled events.
*
* @return \DateTimeZone|string|null
*/
protected function scheduleTimezone()
{
return 'America/Chicago';
}
Hãy nhớ rằng một số múi giờ sử dụng thời gian tiết kiệm ánh sáng ban ngày. Khi các thay đổi về thời gian tiết kiệm ánh sáng ban ngày xảy ra, tác vụ đã lên lịch của bạn có thể chạy hai lần hoặc thậm chí hoàn toàn không chạy. Vì lý do này, chúng tôi khuyên bạn nên tránh lập lịch múi giờ khi có thể.
Ngăn chặn việc chồng chéo nhiệm vụ
Theo mặc định, các tác vụ đã lên lịch sẽ được chạy ngay cả khi phiên bản trước của tác vụ vẫn đang chạy. Để ngăn chặn điều này, bạn có thể sử dụng withoutOverlapping
phương pháp:
$schedule->command('emails:send')->withoutOverlapping();
Trong ví dụ này, emails:send
lệnh Artisan sẽ được chạy mỗi phút nếu nó chưa chạy. Các withoutOverlapping
phương pháp đặc biệt hữu ích nếu bạn có nhiệm vụ mà thay đổi đáng kể trong thời gian thực hiện của họ, ngăn cản bạn từ việc dự đoán một nhiệm vụ nhất định sẽ mất chính xác bao lâu.
Nếu cần, bạn có thể chỉ định bao nhiêu phút phải trôi qua trước khi khóa "không chồng chéo" hết hạn. Theo mặc định, khóa sẽ hết hạn sau 24 giờ:
$schedule->command('emails:send')->withoutOverlapping(10);
Chạy tác vụ trên một máy chủ
Để sử dụng tính năng này, ứng dụng của bạn phải đang sử dụng
database
,memcached
,dynamodb
, hoặcredis
lái xe bộ nhớ cache như tài xế bộ nhớ cache mặc định của ứng dụng của bạn. Ngoài ra, tất cả các máy chủ phải được giao tiếp với cùng một máy chủ bộ đệm trung tâm.
Nếu bộ lập lịch của ứng dụng của bạn đang chạy trên nhiều máy chủ, bạn có thể giới hạn công việc đã lên lịch chỉ được thực thi trên một máy chủ. Ví dụ: giả sử bạn có một nhiệm vụ đã lên lịch tạo một báo cáo mới vào mỗi tối thứ Sáu. Nếu bộ lập lịch tác vụ đang chạy trên ba máy chủ công nhân, tác vụ đã lên lịch sẽ chạy trên cả ba máy chủ và tạo báo cáo ba lần. Không tốt!
Để chỉ ra rằng tác vụ chỉ nên chạy trên một máy chủ, hãy sử dụng onOneServer
phương pháp khi xác định tác vụ đã lên lịch. Máy chủ đầu tiên nhận được nhiệm vụ sẽ bảo mật một khóa nguyên tử đối với công việc để ngăn các máy chủ khác chạy cùng một tác vụ cùng một lúc:
$schedule->command('report:generate')
->fridays()
->at('17:00')
->onOneServer();
Nhiệm vụ nền
Theo mặc định, nhiều tác vụ được lên lịch cùng lúc sẽ thực hiện tuần tự dựa trên thứ tự chúng được xác định trong schedule
phương thức của bạn . Nếu bạn có các tác vụ đang chạy trong thời gian dài, điều này có thể khiến các tác vụ tiếp theo bắt đầu muộn hơn nhiều so với dự kiến. Nếu bạn muốn chạy các tác vụ trong nền để tất cả chúng có thể chạy đồng thời, bạn có thể sử dụng runInBackground
phương pháp:
$schedule->command('analytics:report')
->daily()
->runInBackground();
Các
runInBackground
phương pháp duy nhất có thể được sử dụng khi lịch công việc thông quacommand
vàexec
phương pháp.
Chế độ bảo trì
Các tác vụ đã lên lịch của ứng dụng của bạn sẽ không chạy khi ứng dụng đang ở chế độ bảo trì , vì chúng tôi không muốn các tác vụ của bạn ảnh hưởng đến bất kỳ công việc bảo trì chưa hoàn thành nào mà bạn có thể đang thực hiện trên máy chủ của mình. Tuy nhiên, nếu bạn muốn buộc một tác vụ chạy ngay cả trong chế độ bảo trì, bạn có thể gọi evenInMaintenanceMode
phương thức khi xác định tác vụ:
$schedule->command('emails:send')->evenInMaintenanceMode();
Chạy Trình lập lịch
Bây giờ chúng ta đã học cách xác định các tác vụ đã lên lịch, hãy thảo luận về cách thực sự chạy chúng trên máy chủ của chúng ta. Lệnh schedule:run
Artisan sẽ đánh giá tất cả các tác vụ đã lên lịch của bạn và xác định xem chúng có cần chạy hay không dựa trên thời gian hiện tại của máy chủ.
Vì vậy, khi sử dụng bộ lập lịch của Laravel, chúng tôi chỉ cần thêm một mục nhập cấu hình cron duy nhất vào máy chủ của chúng tôi để chạy schedule:run
lệnh mỗi phút. Nếu bạn không biết cách thêm các mục cron vào máy chủ của mình, hãy xem xét sử dụng một dịch vụ như Laravel Forge có thể quản lý các mục cron cho bạn:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
Chạy bộ lập lịch cục bộ
Thông thường, bạn sẽ không thêm mục nhập cron của bộ lập lịch vào máy phát triển cục bộ của mình. Thay vào đó, bạn có thể sử dụng schedule:work
lệnh Artisan. Lệnh này sẽ chạy ở chế độ nền trước và gọi bộ lập lịch mỗi phút cho đến khi bạn kết thúc lệnh:
php artisan schedule:work
Đầu ra nhiệm vụ
Bộ lập lịch Laravel cung cấp một số phương pháp thuận tiện để làm việc với đầu ra được tạo ra bởi các tác vụ đã lên lịch. Trước tiên, bằng cách sử dụng sendOutputTo
phương pháp này, bạn có thể gửi đầu ra tới một tệp để kiểm tra sau:
$schedule->command('emails:send')
->daily()
->sendOutputTo($filePath);
Nếu bạn muốn nối kết quả đầu ra vào một tệp nhất định, bạn có thể sử dụng appendOutputTo
phương pháp:
$schedule->command('emails:send')
->daily()
->appendOutputTo($filePath);
Sử dụng emailOutputTo
phương pháp này, bạn có thể gửi email đầu ra đến một địa chỉ email bạn chọn. Trước khi gửi email đầu ra của một nhiệm vụ, bạn nên cấu hình các dịch vụ email của Laravel :
$schedule->command('report:generate')
->daily()
->sendOutputTo($filePath)
->emailOutputTo('taylor@example.com');
Nếu bạn chỉ muốn gửi email đầu ra nếu lệnh Artisan hoặc hệ thống đã lên lịch kết thúc bằng mã thoát khác 0, hãy sử dụng emailOutputOnFailure
phương pháp:
$schedule->command('report:generate')
->daily()
->emailOutputOnFailure('taylor@example.com');
Các
emailOutputTo
,emailOutputOnFailure
,sendOutputTo
, vàappendOutputTo
phương pháp độc quyền chocommand
vàexec
phương pháp.
Móc nhiệm vụ
Sử dụng các phương thức before
và after
, bạn có thể chỉ định mã sẽ được thực thi trước và sau khi tác vụ đã lên lịch được thực thi:
$schedule->command('emails:send')
->daily()
->before(function () {
// The task is about to execute...
})
->after(function () {
// The task has executed...
});
Các onSuccess
và onFailure
phương thức cho phép bạn chỉ định mã sẽ được thực thi nếu tác vụ đã lên lịch thành công hay không thành công. Lỗi chỉ ra rằng lệnh Artisan hoặc hệ thống đã lên lịch đã kết thúc bằng mã thoát khác 0:
$schedule->command('emails:send')
->daily()
->onSuccess(function () {
// The task succeeded...
})
->onFailure(function () {
// The task failed...
});
Nếu sản lượng có sẵn từ lệnh của bạn, bạn có thể truy cập vào nó trong của bạn after
, onSuccess
hoặc onFailure
móc theo loại-gợi ý một Illuminate\Support\Stringable
ví dụ như $output
lập luận của định nghĩa đóng cửa của móc của bạn:
use Illuminate\Support\Stringable;
$schedule->command('emails:send')
->daily()
->onSuccess(function (Stringable $output) {
// The task succeeded...
})
->onFailure(function (Stringable $output) {
// The task failed...
});
Ping URL
Sử dụng các phương thức pingBefore
và thenPing
, bộ lập lịch có thể tự động ping một URL nhất định trước hoặc sau khi một tác vụ được thực thi. Phương pháp này hữu ích để thông báo cho một dịch vụ bên ngoài, chẳng hạn như Envoyer , rằng tác vụ đã lên lịch của bạn đang bắt đầu hoặc đã kết thúc thực thi:
$schedule->command('emails:send')
->daily()
->pingBefore($url)
->thenPing($url);
Các phương thức pingBeforeIf
và chỉ thenPingIf
có thể được sử dụng để ping một URL nhất định nếu một điều kiện nhất định là true
:
$schedule->command('emails:send')
->daily()
->pingBeforeIf($condition, $url)
->thenPingIf($condition, $url);
Các phương thức pingOnSuccess
và pingOnFailure
có thể được sử dụng để ping một URL nhất định chỉ khi tác vụ thành công hoặc không thành công. Lỗi chỉ ra rằng lệnh Artisan hoặc hệ thống đã lên lịch đã kết thúc bằng mã thoát khác 0:
$schedule->command('emails:send')
->daily()
->pingOnSuccess($successUrl)
->pingOnFailure($failureUrl);
Tất cả các phương thức ping đều yêu cầu thư viện Guzzle HTTP. Guzzle thường được cài đặt trong tất cả các dự án Laravel mới theo mặc định, nhưng bạn có thể cài đặt Guzzle vào dự án của mình theo cách thủ công bằng trình quản lý gói Composer nếu nó vô tình bị gỡ bỏ:
composer require guzzlehttp/guzzle