- Tác giả

- Name
- Nguyễn Đức Xinh
- Ngày xuất bản
- Ngày xuất bản
Laravel Task Scheduling & Cron Jobs: Lập lịch tác vụ trong Laravel
Task Scheduling Là Gì?
Task scheduling là quá trình tự động hóa các tác vụ lặp lại để chạy vào thời gian hoặc khoảng thời gian cụ thể mà không cần can thiệp thủ công. Các trường hợp sử dụng phổ biến bao gồm:
- Gửi email báo cáo theo lịch
- Dọn dẹp các bản ghi cũ trong database
- Tạo báo cáo phân tích hàng ngày
- Backup database
- Xử lý các tác vụ hàng loạt (batch operations)
- Đồng bộ dữ liệu với API bên ngoài
Trong quá khứ, các developer thường sử dụng cron (bộ lập lịch job dựa trên thời gian trong hệ thống Unix-like) để schedule các task, đòi hỏi quyền truy cập server trực tiếp và quản lý cron entry thủ công.
Hiểu Về Cron Jobs
Trước khi tìm hiểu về hệ thống scheduling của Laravel, hãy hiểu những kiến thức cơ bản về cron.
Cú Pháp Cron
Một cron expression bao gồm năm trường thời gian:
* * * * *
│ │ │ │ │
│ │ │ │ └─── Ngày trong tuần (0-7, Chủ nhật = 0 hoặc 7)
│ │ │ └───── Tháng (1-12)
│ │ └─────── Ngày trong tháng (1-31)
│ └───────── Giờ (0-23)
└─────────── Phút (0-59)
Các Ví Dụ Cron Phổ Biến
| Expression | Mô Tả |
|---|---|
* * * * * |
Mỗi phút |
0 * * * * |
Mỗi giờ |
0 0 * * * |
Hàng ngày lúc nửa đêm |
0 0 * * 0 |
Hàng tuần vào Chủ nhật |
0 0 1 * * |
Hàng tháng vào ngày 1 |
*/5 * * * * |
Mỗi 5 phút |
0 9-17 * * 1-5 |
Các ngày trong tuần từ 9 AM đến 5 PM |
Hạn Chế Của Cron Truyền Thống
- Task không được lưu trong source control
- Yêu cầu quyền SSH để quản lý
- Không có khả năng hiển thị định nghĩa task
- Khó test ở local
- Không có xử lý lỗi tích hợp sẵn
- Cú pháp phức tạp cho logic điều kiện
Giải Pháp Task Scheduling Của Laravel
Scheduler của Laravel cung cấp một API fluent, elegant để quản lý các scheduled task hoàn toàn trong code ứng dụng của bạn. Lợi ích bao gồm:
- Version Control: Tất cả schedule đều trong codebase
- Cú Pháp Rõ Ràng: Các method dễ đọc, có thể chain
- Chỉ Cần Một Cron Entry: Chỉ cần một cron job duy nhất
- Dễ Test: Test schedule mà không cần truy cập server
- Xử Lý Lỗi Tích Hợp: Hooks cho success/failure
- Ngăn Chặn Overlap: Tránh thực thi task đồng thời
- Distributed Scheduling: Chạy task trên single server trong môi trường multi-server
Bắt Đầu Với Laravel Scheduler
Bước 1: Định Nghĩa Scheduled Tasks
Tất cả scheduled tasks được định nghĩa trong file routes/console.php hoặc trong phương thức schedule của App\Console\Kernel:
routes/console.php
<?php
use Illuminate\Support\Facades\Schedule;
use Illuminate\Support\Facades\DB;
// Schedule một closure
Schedule::call(function () {
DB::table('recent_users')->delete();
})->daily();
// Schedule một Artisan command
Schedule::command('emails:send')->dailyAt('13:00');
// Schedule một queued job
Schedule::job(new ProcessPodcast)->everyFiveMinutes();
// Schedule một shell command
Schedule::exec('node /home/forge/script.js')->daily();
app/Console/Kernel.php
<?php
class Kernel extends ConsoleKernel
{
protected function schedule(Schedule $schedule)
{
$schedule->command('emails:send')->dailyAt('13:00')->withoutOverlapping();
}
}
Ngoài ra, bạn có thể định nghĩa schedule trong bootstrap/app.php:
use Illuminate\Console\Scheduling\Schedule;
->withSchedule(function (Schedule $schedule) {
$schedule->call(new DeleteRecentUsers)->daily();
})
Bước 2: Thêm Single Cron Entry
Thêm cron entry một lần duy nhất này vào server của bạn:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
Điều này chạy mỗi phút và Laravel đánh giá task nào nên thực thi dựa trên schedule của chúng.
Bước 3: Xem Các Scheduled Tasks
Liệt kê tất cả scheduled tasks và thời gian chạy tiếp theo:
php artisan schedule:list
Các Tùy Chọn Schedule Frequency
Laravel cung cấp các tùy chọn frequency rộng rãi đáp ứng hầu hết mọi nhu cầu scheduling.
Frequency Dựa Trên Thời Gian
// Mỗi giây (sub-minute scheduling)
Schedule::command('monitor:check')->everySecond();
Schedule::command('monitor:check')->everyFiveSeconds();
Schedule::command('monitor:check')->everyTenSeconds();
// Mỗi phút
Schedule::command('process:orders')->everyMinute();
Schedule::command('process:orders')->everyFiveMinutes();
Schedule::command('process:orders')->everyTenMinutes();
// Mỗi giờ
Schedule::command('report:generate')->hourly();
Schedule::command('report:generate')->hourlyAt(17); // Tại phút 17
Schedule::command('report:generate')->everyTwoHours();
Schedule::command('report:generate')->everyThreeHours();
// Hàng ngày
Schedule::command('backup:run')->daily();
Schedule::command('backup:run')->dailyAt('13:00');
Schedule::command('backup:run')->twiceDaily(1, 13); // 1:00 & 13:00
// Hàng tuần
Schedule::command('report:weekly')->weekly();
Schedule::command('report:weekly')->weeklyOn(1, '8:00'); // Thứ 2 lúc 8 AM
// Hàng tháng
Schedule::command('invoice:generate')->monthly();
Schedule::command('invoice:generate')->monthlyOn(4, '15:00'); // Ngày 4 lúc 3 PM
Schedule::command('invoice:generate')->twiceMonthly(1, 16, '13:00');
// Hàng quý và hàng năm
Schedule::command('report:quarterly')->quarterly();
Schedule::command('report:annual')->yearly();
Schedule::command('report:annual')->yearlyOn(6, 1, '17:00'); // 1 tháng 6
Custom Cron Expressions
Để kiểm soát hoàn toàn, sử dụng cú pháp cron tùy chỉnh:
Schedule::command('emails:send')->cron('0 */6 * * *'); // Mỗi 6 giờ
Ràng Buộc Ngày và Thời Gian
// Các ngày cụ thể trong tuần
Schedule::command('emails:send')
->hourly()
->days([0, 3]); // Chủ nhật và Thứ tư
// Ngày trong tuần/cuối tuần
Schedule::command('report:generate')
->weekdays()
->at('9:00');
Schedule::command('maintenance:run')
->weekends()
->at('3:00');
// Các method cho ngày cụ thể
Schedule::command('sales:report')
->mondays()
->at('8:00');
// Khoảng thời gian
Schedule::command('process:data')
->hourly()
->between('8:00', '17:00');
Schedule::command('maintenance:run')
->hourly()
->unlessBetween('9:00', '18:00');
Kỹ Thuật Scheduling Nâng Cao
Ngăn Chặn Task Overlaps
Ngăn một task chạy nếu instance trước đó vẫn đang thực thi:
Schedule::command('emails:send')
->everyMinute()
->withoutOverlapping();
// Thời gian expiration tùy chỉnh (tính bằng phút)
Schedule::command('process:large-file')
->everyFiveMinutes()
->withoutOverlapping(10); // Lock hết hạn sau 10 phút
Xóa các lock bị kẹt:
php artisan schedule:clear-cache
Chạy Tasks Trên Single Server
Trong môi trường multi-server, đảm bảo task chỉ chạy trên một server:
Schedule::command('report:generate')
->fridays()
->at('17:00')
->onOneServer();
Yêu cầu:
- Cache driver:
database,memcached,dynamodb, hoặcredis - Tất cả server phải chia sẻ cùng một cache server
Đặt tên cho single-server jobs:
Schedule::job(new CheckUptime('https://laravel.com'))
->name('check_uptime:laravel.com')
->everyFiveMinutes()
->onOneServer();
Schedule::job(new CheckUptime('https://vapor.laravel.com'))
->name('check_uptime:vapor.laravel.com')
->everyFiveMinutes()
->onOneServer();
Thực Thi Task Ở Background
Chạy task ở background để tránh blocking:
Schedule::command('analytics:report')
->daily()
->runInBackground();
// Kết hợp với sub-minute scheduling
Schedule::command('users:delete')
->everyTenSeconds()
->runInBackground();
Thực Thi Task Có Điều Kiện
Thực thi task dựa trên điều kiện tùy chỉnh:
// Chỉ chạy khi điều kiện là true
Schedule::command('emails:send')
->daily()
->when(function () {
return DB::table('settings')->where('key', 'mail_enabled')->value('value');
});
// Bỏ qua khi điều kiện là true
Schedule::command('backup:run')
->daily()
->skip(function () {
return now()->isWeekend();
});
// Ràng buộc environment
Schedule::command('report:generate')
->daily()
->environments(['staging', 'production']);
Quản Lý Timezone
Chỉ định timezone cho scheduled tasks:
Schedule::command('report:generate')
->timezone('America/New_York')
->at('2:00');
Cấu hình timezone global trong config/app.php:
'timezone' => 'UTC',
'schedule_timezone' => 'America/Chicago',
⚠️ Cảnh báo: Hãy cẩn thận với thay đổi giờ mùa hè, vì task có thể chạy hai lần hoặc không chạy.
Xử Lý Maintenance Mode
Mặc định, task không chạy trong maintenance mode. Ghi đè điều này:
Schedule::command('critical:task')
->everyFiveMinutes()
->evenInMaintenanceMode();
Schedule Groups
Nhóm các task với cấu hình tương tự để tránh lặp lại:
Schedule::daily()
->onOneServer()
->timezone('America/New_York')
->group(function () {
Schedule::command('emails:send --force');
Schedule::command('emails:prune');
Schedule::command('reports:daily');
});
Quản Lý Task Output
Ghi Log Output Vào Files
// Ghi đè file
Schedule::command('emails:send')
->daily()
->sendOutputTo($filePath);
// Append vào file
Schedule::command('emails:send')
->daily()
->appendOutputTo($filePath);
Email Output
// Email output khi hoàn thành
Schedule::command('report:generate')
->daily()
->sendOutputTo($filePath)
->emailOutputTo('admin@example.com');
// Email chỉ khi thất bại
Schedule::command('report:generate')
->daily()
->emailOutputOnFailure('admin@example.com');
Lưu ý: Cấu hình Laravel mail services trước khi sử dụng email output.
Task Hooks và Callbacks
Before và After Hooks
Schedule::command('emails:send')
->daily()
->before(function () {
Log::info('Starting email job');
// Chuẩn bị resources
})
->after(function () {
Log::info('Email job completed');
// Dọn dẹp resources
});
Success và Failure Handlers
Schedule::command('import:data')
->daily()
->onSuccess(function () {
Log::info('Import succeeded');
Notification::send(User::admins(), new ImportSuccessful());
})
->onFailure(function () {
Log::error('Import failed');
Notification::send(User::admins(), new ImportFailed());
});
Truy Cập Task Output Trong Hooks
use Illuminate\Support\Stringable;
Schedule::command('report:generate')
->daily()
->onSuccess(function (Stringable $output) {
// Xử lý output
if ($output->contains('Error')) {
Log::warning('Report generated with warnings', [
'output' => $output->toString()
]);
}
});
Webhook Pinging
Thông báo cho các service bên ngoài khi task chạy:
Schedule::command('emails:send')
->daily()
->pingBefore($url) // Trước khi thực thi
->thenPing($url); // Sau khi thực thi
// Conditional pinging
Schedule::command('backup:run')
->daily()
->pingOnSuccess($successUrl)
->pingOnFailure($failureUrl);
// Webhook calls có điều kiện
Schedule::command('sync:data')
->daily()
->pingBeforeIf($condition, $url)
->pingOnSuccessIf($condition, $successUrl);
Sub-Minute Scheduling
Laravel hỗ trợ task chạy thường xuyên hơn một lần mỗi phút:
Schedule::call(function () {
DB::table('recent_users')->delete();
})->everySecond();
Schedule::command('monitor:check')
->everyFiveSeconds();
Best practices:
- Dispatch queued jobs để xử lý thực tế
- Sử dụng
runInBackground()để tránh blocking - Tránh các sub-minute task chạy lâu
use App\Jobs\DeleteRecentUsers;
Schedule::job(new DeleteRecentUsers)->everyTenSeconds();
Schedule::command('users:delete')
->everyTenSeconds()
->runInBackground();
Ngắt Sub-Minute Tasks
Trong quá trình deploy, ngắt các scheduler đang chạy:
php artisan schedule:interrupt
Thêm lệnh này vào deployment script để ngăn code cũ chạy.
Chạy Scheduler Ở Local
Để phát triển ở local, sử dụng lệnh schedule:work thay vì thiết lập cron:
php artisan schedule:work
Lệnh này chạy ở foreground và invoke scheduler mỗi phút cho đến khi bị terminate.
Scheduling Các Loại Task Khác Nhau
1. Scheduling Artisan Commands
// Theo tên command
Schedule::command('emails:send Taylor --force')->daily();
// Theo tên class với arguments
use App\Console\Commands\SendEmailsCommand;
Schedule::command(SendEmailsCommand::class, ['Taylor', '--force'])->daily();
2. Scheduling Artisan Closure Commands
Artisan::command('delete:recent-users', function () {
DB::table('recent_users')->delete();
})->purpose('Delete recent users')->daily();
// Với arguments
Artisan::command('emails:send {user} {--force}', function ($user) {
// Logic gửi email
})->purpose('Send emails to specified user')
->schedule(['Taylor', '--force'])
->daily();
3. Scheduling Queued Jobs
use App\Jobs\Heartbeat;
Schedule::job(new Heartbeat)->everyFiveMinutes();
// Chỉ định queue và connection
Schedule::job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();
4. Scheduling Shell Commands
Schedule::exec('node /home/forge/script.js')->daily();
Schedule::exec('python /scripts/backup.py')
->daily()
->sendOutputTo('/logs/backup.log');
5. Scheduling Invokable Objects
class DeleteRecentUsers
{
public function __invoke()
{
DB::table('recent_users')->delete();
}
}
Schedule::call(new DeleteRecentUsers)->daily();
Scheduled Events
Laravel dispatch các events trong suốt vòng đời scheduling. Bạn có thể tạo listeners cho:
| Event | Mô Tả |
|---|---|
ScheduledTaskStarting |
Trước khi task bắt đầu |
ScheduledTaskFinished |
Sau khi task hoàn thành |
ScheduledBackgroundTaskFinished |
Background task hoàn thành |
ScheduledTaskSkipped |
Task bị bỏ qua |
ScheduledTaskFailed |
Task thất bại |
Ví dụ listener:
namespace App\Listeners;
use Illuminate\Console\Events\ScheduledTaskFailed;
use Illuminate\Support\Facades\Log;
class LogScheduledTaskFailed
{
public function handle(ScheduledTaskFailed $event)
{
Log::error('Scheduled task failed', [
'task' => $event->task->command,
'exception' => $event->exception,
]);
}
}
Xử Lý Các Vấn Đề Thường Gặp
Scheduler Không Chạy
Kiểm tra cron đã được cấu hình:
crontab -l
Test thủ công:
php artisan schedule:run
Kiểm tra logs:
tail -f storage/logs/laravel.log
Tasks Chạy Nhiều Lần
- Đảm bảo
onOneServer()được sử dụng trong thiết lập multi-server - Xác minh cấu hình cache cho distributed locks
- Kiểm tra các cron entry trùng lặp
Overlapping Tasks
// Thêm overlap prevention
Schedule::command('long-running-task')
->everyMinute()
->withoutOverlapping();
Tasks Không Chạy Đúng Giờ
- Xác minh timezone của server:
date - Kiểm tra cấu hình
schedule_timezone - Sử dụng timezone cụ thể trong định nghĩa task
Xóa Stuck Locks
php artisan schedule:clear-cache
So Sánh: Traditional Cron vs Laravel Scheduler
| Tính Năng | Traditional Cron | Laravel Scheduler |
|---|---|---|
| Vị Trí Cấu Hình | Server crontab | Application code |
| Version Control | ❌ Không | ✅ Có |
| Cú Pháp | Cron expressions phức tạp | Fluent, readable methods |
| Testing | Khó khăn | Dễ dàng với Artisan commands |
| Conditional Logic | Giới hạn | Hỗ trợ đầy đủ PHP logic |
| Error Handling | Cần thiết lập thủ công | Built-in hooks |
| Output Management | Redirection phức tạp | Simple method calls |
| Multi-server Support | Phối hợp thủ công | Built-in onOneServer() |
| Overlap Prevention | Manual flock/pidfiles | Built-in withoutOverlapping() |
| Sub-minute Tasks | Không hỗ trợ | Được hỗ trợ |
| Deployment | Cần quyền truy cập server | Deploy với code |
Kết Luận
Hệ thống task scheduling của Laravel biến đổi quản lý cron job truyền thống thành một giải pháp elegant, có thể test và dễ bảo trì. Bằng cách thành thạo scheduler, bạn có thể:
- Tự động hóa các tác vụ lặp lại với cấu hình tối thiểu
- Duy trì schedules trong version control cùng với application code
- Triển khai logic scheduling phức tạp với cú pháp fluent, dễ đọc
- Xử lý distributed systems với single-server execution tích hợp sẵn
- Giám sát và debug tasks với hooks và events toàn diện
- Scale một cách tự tin biết rằng overlap prevention và queue integration đã được tích hợp sẵn
Dù bạn đang xây dựng một blog đơn giản cần cleanup hàng ngày hay một ứng dụng SaaS phức tạp với yêu cầu scheduling phức tạp, scheduler của Laravel cung cấp các công cụ bạn cần để tự động hóa hiệu quả và đáng tin cậy.
Hãy bắt đầu đơn giản với các schedule cơ bản, sau đó dần dần triển khai các tính năng nâng cao như single-server execution, overlap prevention, và giám sát toàn diện khi ứng dụng của bạn phát triển. Scheduler sẽ scale theo nhu cầu của bạn trong khi giữ code sạch sẽ và dễ bảo trì.
Tài Nguyên Bổ Sung
- Official Laravel Scheduling Documentation
- Laravel Horizon - Để giám sát queue
- Laravel Telescope - Để debug scheduled tasks
- Cron Expression Reference - Interactive cron syntax helper
