🎯 Mục tiêu bài học
Trong các bài trước, chúng ta đã làm việc trực tiếp với:
Controller
Model
Event
Listener
Observer
Notification
Mail
Tuy nhiên, có một vấn đề:
Nếu một công việc mất nhiều thời gian, chúng ta có muốn người dùng phải chờ cho đến khi công việc hoàn thành hay không?
Ví dụ:
Người dùng tạo bài viết
↓
Laravel lưu bài viết
↓
Gửi email
↓
Tạo thumbnail
↓
Tạo thông báo
↓
Ghi log
↓
Trả Response
Nếu tất cả đều chạy ngay trong HTTP Request, người dùng có thể phải chờ rất lâu.
Laravel Queue giải quyết vấn đề này bằng cách đưa những công việc cần xử lý sau vào hàng đợi.
Mô hình:
HTTP Request
│
▼
Lưu dữ liệu
│
▼
Dispatch Job
│
├──────────────► Response
│
▼
Queue
│
▼
Worker
│
▼
Xử lý công việc📑 MỤC LỤC
📦 Tạo Job
1. 🚀 Queue là gì?
Queue nghĩa là hàng đợi.
Trong Laravel, Queue cho phép chúng ta đưa một công việc vào hàng đợi để xử lý sau thay vì xử lý ngay trong HTTP Request.
Ví dụ:
User
│
│ Tạo bài viết
▼
Laravel
│
├── Lưu Post
│
├── Dispatch Job
│
▼
Response
│
│
▼
Queue
│
▼
Worker
│
▼
Gửi Email
Ví dụ công việc phù hợp với Queue:
📧 Gửi email
🖼️ Resize ảnh
📊 Tạo báo cáo
📦 Import dữ liệu
🔔 Gửi notification
🔄 Đồng bộ dữ liệu
🧮 Xử lý dữ liệu lớn
2. ⚙️ Laravel Queue hoạt động như thế nào?
Queue có thể hình dung rất đơn giản:
JOB
│
▼
QUEUE
│
▼
WORKER
│
▼
PROCESSING
Ví dụ:
SendWelcomeEmail
↓
Queue
↓
Worker
↓
Send Email
Điểm quan trọng:
Queue không tự xử lý Job.
Queue chỉ lưu công việc đang chờ.
Một Worker phải lấy Job ra và xử lý.
3. 🧩 Ba thành phần: Job — Queue — Worker
Đây là ba khái niệm quan trọng nhất.
Job
Job mô tả:
Công việc cần thực hiện.
Ví dụ:
SendWelcomeEmail
GenerateThumbnail
GenerateReport
Queue
Queue là:
Nơi lưu những Job đang chờ xử lý.
Ví dụ:
Queue
├── Job A
├── Job B
├── Job C
└── Job D
Worker
Worker là:
Process chạy liên tục để lấy Job từ Queue và xử lý.
Queue
│
▼
Worker
│
├── Job A
├── Job B
└── Job C
Có thể ghi nhớ:
Job = Làm gì?
Queue = Chờ ở đâu?
Worker = Ai làm?
4. 🗄️ Queue Driver là gì?
Queue Driver quyết định Laravel sẽ lưu Job ở đâu.
Trong Laravel, cấu hình Queue nằm trong:
config/queue.php
Ví dụ:
QUEUE_CONNECTION=database
Có nghĩa Laravel sử dụng:
database
làm Queue Connection mặc định.
Có thể hình dung:
Laravel
│
▼
Queue
│
▼
Driver
│
├── database
├── redis
├── sqs
└── sync
5. 🔄 Các Queue Driver phổ biến
Một số Queue Connection thường gặp:
sync
Job chạy ngay lập tức.
QUEUE_CONNECTION=sync
Mô hình:
Request
↓
Job
↓
Xử lý ngay
↓
Response
Không thực sự có hàng đợi.
database
Job được lưu vào database.
QUEUE_CONNECTION=database
Mô hình:
Job
↓
jobs table
↓
Worker
↓
Processing
Đây là lựa chọn rất dễ học và phù hợp để thực hành.
redis
Job được lưu trong Redis.
QUEUE_CONNECTION=redis
Redis thường phù hợp với hệ thống cần Queue có hiệu năng cao.
Amazon SQS
Laravel cũng hỗ trợ Amazon SQS cho hệ thống triển khai trên AWS.
6. 🔧 Cấu hình Queue
File cấu hình chính:
config/queue.php
Connection mặc định thường lấy từ:
QUEUE_CONNECTION=database
Sau khi thay đổi .env, có thể cần xóa config cache:
php artisan config:clear
Kiểm tra cấu hình:
php artisan config:show queue
Tư duy:
.env
↓
config/queue.php
↓
Laravel Queue
7. 🛠️ Tạo bảng Queue
Nếu sử dụng Database Queue, Laravel cần bảng để lưu Job.
Trong các project Laravel mới, migration liên quan đến Queue có thể đã tồn tại sẵn.
Trước tiên nên kiểm tra:
database/migrations/
Nếu project chưa có migration cho Queue, có thể tạo bằng:
php artisan make:queue-table
Sau đó:
php artisan migrate
Laravel sẽ có bảng:
jobs
Có thể hình dung:
jobs
├── id
├── queue
├── payload
├── attempts
├── reserved_at
└── available_at
Job được dispatch sẽ được lưu vào đây khi sử dụng Database Queue.
8. 📦 Tạo Job
Laravel cung cấp Artisan command:
php artisan make:job SendWelcomeEmail
Laravel tạo:
app/
└── Jobs/
└── SendWelcomeEmail.php
Một Job có thể được viết:
<?php
namespace App\Jobs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
class SendWelcomeEmail implements ShouldQueue
{
use Queueable;
public function handle(): void
{
// Gửi email
}
}
Điểm quan trọng là:
implements ShouldQueue
Nó cho Laravel biết Job này được thiết kế để chạy thông qua Queue.
9. 🎯 handle() là gì?
handle() là nơi Laravel thực hiện công việc của Job.
Ví dụ:
public function handle(): void
{
// Gửi email
}
Hoặc:
public function handle(): void
{
User::query()
->where('is_active', true)
->update([
'last_synced_at' => now(),
]);
}
Tư duy:
dispatch()
↓
Queue
↓
Worker
↓
handle()
Worker lấy Job ra và Laravel gọi:
$job->handle();
theo cơ chế nội bộ của Queue.
10. 📥 Truyền dữ liệu vào Job
Job thường cần dữ liệu.
Ví dụ muốn gửi email cho một User.
class SendWelcomeEmail implements ShouldQueue
{
use Queueable;
public function __construct(
public int $userId
) {
}
public function handle(): void
{
$user = User::findOrFail($this->userId);
// Gửi email
}
}
Khi dispatch:
SendWelcomeEmail::dispatch($user->id);
Dữ liệu:
$user->id
↓
Job constructor
↓
$userId
↓
handle()
11. 🚀 Dispatch Job
Sau khi tạo Job:
SendWelcomeEmail::dispatch($user->id);
Laravel đưa Job vào Queue.
Nếu:
QUEUE_CONNECTION=database
thì Job sẽ được lưu vào database.
Mô hình:
Controller
│
▼
dispatch()
│
▼
jobs
│
▼
Worker
│
▼
handle()
Đây là một trong những dòng code quan trọng nhất của Queue:
SendWelcomeEmail::dispatch($user->id);
12. 🌐 Request hoạt động như thế nào khi dùng Queue?
Đây là lý do chính chúng ta sử dụng Queue.
Không dùng Queue:
Browser
│
▼
Controller
│
▼
Gửi email
│
▼
Xử lý ảnh
│
▼
Tạo report
│
▼
Response
Người dùng phải chờ.
Có Queue:
Browser
│
▼
Controller
│
├── Lưu dữ liệu
│
├── Dispatch Job
│
▼
Response nhanh
Sau đó:
Queue
↓
Worker
↓
Job
↓
Xử lý
Đây là tư duy quan trọng:
Request không cần trực tiếp làm mọi việc.
13. 👷 Chạy Queue Worker
Dispatch Job chưa có nghĩa là Job đã được xử lý.
Nếu dùng Database Queue, cần Worker:
php artisan queue:work
Worker sẽ:
jobs
↓
Job
↓
handle()
↓
done
Terminal có thể hiển thị:
INFO Processing jobs from the [default] queue.
Để học Queue, nên mở một terminal riêng chạy Worker.
14. 🔁 Worker chạy liên tục
Worker không chỉ chạy một Job.
Thông thường:
php artisan queue:work
sẽ chạy liên tục:
Job A
↓
Job B
↓
Job C
↓
Job D
↓
...
Có thể giới hạn số Job:
php artisan queue:work --once
Khi đó Worker xử lý một Job rồi kết thúc.
Mô hình:
queue:work
│
├── Job A
├── Job B
├── Job C
└── Job D
15. ⏳ Delay Job
Không phải Job nào cũng cần chạy ngay.
Laravel cho phép delay Job.
Ví dụ:
SendWelcomeEmail::dispatch($user->id)
->delay(now()->addMinutes(5));
Mô hình:
dispatch
↓
Queue
↓
5 phút
↓
Worker xử lý
Ví dụ thực tế:
User đăng ký
↓
Chờ 5 phút
↓
Gửi email hướng dẫn
Delay rất hữu ích cho những công việc không cần thực hiện ngay lập tức.
16. 📚 Xử lý nhiều loại Job
Một ứng dụng thực tế thường có nhiều Job.
Ví dụ Blog CMS:
app/Jobs/
├── SendPostNotification.php
├── GeneratePostThumbnail.php
├── GeneratePostReport.php
└── SendWelcomeEmail.php
Queue:
Queue
│
├── SendPostNotification
├── GeneratePostThumbnail
├── GeneratePostReport
└── SendWelcomeEmail
Worker có thể xử lý từng Job.
Khi hệ thống lớn hơn, chúng ta có thể chia thành nhiều Queue:
notifications
images
reports
emails
17. 🔗 Queue kết hợp Event & Listener
Queue rất hay khi kết hợp với Event & Listener.
Ví dụ:
PostPublished
↓
Listener
↓
SendPostNotification Job
↓
Queue
↓
Worker
Controller chỉ cần:
event(new PostPublished($post));
Listener có thể dispatch Job:
SendPostNotification::dispatch($post->id);
Mô hình:
Controller
↓
Event
↓
Listener
↓
Job
↓
Queue
↓
Worker
Điều này giúp Controller không phải chứa quá nhiều logic.
18. 📝 Ứng dụng Queue trong Blog CMS
Hãy áp dụng Queue vào Blog CMS.
Khi Admin publish bài viết:
Admin
↓
Publish Post
↓
PostController
↓
Save Post
↓
Event: PostPublished
↓
Listener
↓
Dispatch Jobs
Có thể có:
SendPostNotification
GeneratePostThumbnail
UpdateSearchIndex
Queue:
notifications
images
search
Worker xử lý phía sau.
Kết quả:
Admin
↓
Publish
↓
Response nhanh
↓
Queue
↓
Email
↓
Thumbnail
↓
Search Index
Đây là một ứng dụng thực tế rất phù hợp với Blog CMS.
19. ❌ Failed Jobs
Job không phải lúc nào cũng thành công.
Ví dụ:
Worker
↓
Send Email
↓
SMTP lỗi
↓
Job FAILED
Laravel có cơ chế quản lý Failed Jobs.
Một Job có thể thất bại vì:
API không hoạt động
Database lỗi
Email server lỗi
Network timeout
Dữ liệu không hợp lệ
Exception trong
handle()
Không nên xem Failed Job là điều bất thường hoàn toàn.
Trong hệ thống Queue thực tế:
Failure là một phần cần được thiết kế để xử lý.
20. 🔍 Kiểm tra Failed Jobs
Laravel hỗ trợ bảng:
failed_jobs
Nếu project chưa có migration Failed Jobs, có thể tạo:
php artisan make:queue-failed-table
Sau đó:
php artisan migrate
Kiểm tra Failed Jobs:
php artisan queue:failed
Laravel sẽ hiển thị những Job thất bại.
Ví dụ:
UUID
Connection
Queue
Class
Failed At
21. 🔄 Retry Job
Một Job thất bại không nhất thiết phải bỏ đi.
Có thể retry:
php artisan queue:retry all
Hoặc retry một Job cụ thể:
php artisan queue:retry <uuid>
Mô hình:
Job
↓
FAILED
↓
Retry
↓
Worker
↓
SUCCESS
Đây là một trong những ưu điểm lớn của Queue.
22. 💥 Xử lý Job thất bại với failed()
Job có thể định nghĩa:
public function failed(?Throwable $exception): void
{
// Xử lý khi Job thất bại
}
Ví dụ:
public function failed(?Throwable $exception): void
{
Log::error(
'SendWelcomeEmail failed',
[
'user_id' => $this->userId,
'error' => $exception?->getMessage(),
]
);
}
Có thể sử dụng để:
Ghi Log
Gửi cảnh báo
Cập nhật trạng thái
Thông báo Admin
Mô hình:
handle()
↓
Exception
↓
failed()
23. ⭐ Queue Priority
Khi có nhiều Queue:
high
default
low
Worker có thể ưu tiên queue:
php artisan queue:work --queue=high,default,low
Worker sẽ ưu tiên:
high
↓
default
↓
low
Ví dụ:
high
├── Payment
└── SecurityNotification
default
├── Email
└── Notification
low
├── Report
└── Analytics
Điểm cần nhớ:
Priority quyết định Worker xử lý queue nào trước.
24. 🧭 Queue Routing trong Laravel 13
Laravel 13 bổ sung khả năng Queue Routing theo Job class thông qua:
Queue::route(...)
Trước tiên:
use Illuminate\Support\Facades\Queue;
Sau đó có thể khai báo:
Queue::route(
SendWelcomeEmail::class,
connection: 'redis',
queue: 'emails',
);
Có nghĩa:
SendWelcomeEmail
↓
Redis
↓
emails
Khi dispatch:
SendWelcomeEmail::dispatch($user->id);
Job có thể sử dụng route mặc định đã khai báo.
Routing khác Priority
Đừng nhầm hai khái niệm:
Queue Routing
↓
Job đi đâu?
Queue Priority
↓
Queue nào xử lý trước?
Ví dụ:
GeneratePostThumbnail
│
│ Routing
▼
images
│
│ Worker Priority
▼
Worker xử lý queue
Queue Routing đặc biệt hữu ích khi ứng dụng có nhiều loại Job:
emails
images
reports
notifications
Laravel 13 cho phép chúng ta tổ chức việc định tuyến Job rõ ràng hơn thay vì phải lặp lại cấu hình queue ở nhiều nơi.
25. 🖥️ Queue Worker trong Production
Trong môi trường phát triển, chúng ta có thể chạy:
php artisan queue:work
Nhưng Production không nên phụ thuộc vào việc người quản trị mở Terminal rồi chạy Worker thủ công.
Thông thường Worker được quản lý bởi process manager như:
Supervisor
Mô hình:
Production Server
│
▼
Supervisor
│
▼
queue:work
│
▼
Queue
Nếu Worker bị crash, process manager có thể khởi động lại.
Production còn cần quan tâm:
Worker restart
Memory limit
Timeout
Retry
Failed Jobs
Logging
Monitoring
Với hệ thống Laravel lớn, Laravel Horizon cũng là một công cụ quan trọng khi sử dụng Redis Queue.
26. ⚡ Queue có thực sự chạy Background không?
Đây là một hiểu lầm phổ biến.
Khi viết:
SendWelcomeEmail::dispatch();
không có nghĩa PHP process hiện tại tự biến thành một background thread.
Đúng hơn:
HTTP Request
│
▼
Dispatch Job
│
▼
Queue lưu Job
│
▼
HTTP Request kết thúc
Một process khác:
Worker
↓
Lấy Job
↓
handle()
Vì vậy:
Queue tạo ra cơ chế xử lý bất đồng bộ thông qua một Worker riêng.
Đây là lý do cần chạy:
php artisan queue:work
27. 🔀 So sánh sync và database
Đây là ví dụ rất dễ hiểu.
sync
QUEUE_CONNECTION=sync
Mô hình:
Request
↓
dispatch()
↓
Job chạy ngay
↓
Response
database
QUEUE_CONNECTION=database
Mô hình:
Request
↓
dispatch()
↓
jobs
↓
Response
Worker
↓
jobs
↓
handle()
So sánh:
| sync | database | |
|---|---|---|
| Lưu Job | Không | Có |
| Worker cần thiết | Không | Có |
| Xử lý bất đồng bộ | Không | Có |
| Dễ học | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| Production Queue | Không phù hợp | Có thể |
Nếu đang học Queue:
QUEUE_CONNECTION=database
là một lựa chọn rất dễ quan sát.
28. 🧪 Ví dụ Queue hoàn chỉnh
Hãy xây dựng một Job gửi thông báo khi bài viết được publish.
Bước 1 — Tạo Job
php artisan make:job SendPostNotification
Bước 2 — Job
<?php
namespace App\Jobs;
use App\Models\Post;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
class SendPostNotification implements ShouldQueue
{
use Queueable;
public function __construct(
public int $postId
) {
}
public function handle(): void
{
$post = Post::findOrFail($this->postId);
// Gửi notification
// ...
}
}
Bước 3 — Dispatch
Trong Controller:
SendPostNotification::dispatch($post->id);
Bước 4 — Chạy Worker
php artisan queue:work
Bước 5 — Quy trình
Admin
↓
Publish Post
↓
Controller
↓
dispatch()
↓
jobs
↓
Response
Worker
↓
jobs
↓
SendPostNotification
↓
handle()
↓
Done
Đây chính là Queue từ đầu đến cuối.
29. 🏗️ Queue trong kiến trúc Laravel
Queue không đứng một mình.
Nó có thể kết hợp:
Controller
↓
Service
↓
Event
↓
Listener
↓
Job
↓
Queue
↓
Worker
↓
External Service
Ví dụ Blog CMS:
PostController
↓
Publish Post
↓
PostPublished Event
↓
Listener
↓
SendPostNotification
↓
Queue
↓
Worker
↓
Email / Notification
Mỗi thành phần có một trách nhiệm.
Đây là tư duy quan trọng của Laravel:
Không phải Controller phải làm tất cả mọi việc.
30. 💡 Khi nào nên sử dụng Queue?
Nên dùng Queue khi công việc:
⏱️ Mất nhiều thời gian
Ví dụ:
Generate PDF
Process Image
Export Excel
📧 Không cần hoàn thành ngay
Ví dụ:
Send Email
Send Notification
Generate Report
📦 Xử lý số lượng lớn
Ví dụ:
100.000 users
↓
100.000 emails
Không nên xử lý tất cả trong một HTTP Request.
Có thể chia thành:
User 1 → Job
User 2 → Job
User 3 → Job
...
31. 🚫 Khi nào không nên dùng Queue?
Không phải công việc nào cũng cần Queue.
Ví dụ:
$post = Post::create($data);
Việc này cần hoàn thành ngay để biết Post đã được tạo hay chưa.
Không nên biến mọi thứ thành Queue.
Ví dụ:
Validate Request
↓
Queue
↓
Save Post
là không hợp lý trong một Request thông thường.
Nguyên tắc:
Nếu Response cần kết quả ngay, thường không nên đẩy công việc đó sang Queue.
Queue phù hợp hơn với công việc:
"Không cần hoàn thành ngay."
32. 🐛 Các lỗi Queue thường gặp
Lỗi 1 — Dispatch nhưng Job không chạy
Kiểm tra Worker:
php artisan queue:work
Lỗi 2 — Đang dùng sync
Kiểm tra:
QUEUE_CONNECTION=sync
Nếu đang dùng sync, Job sẽ chạy ngay.
Lỗi 3 — Không có bảng jobs
Nếu dùng Database Queue:
jobs
phải tồn tại.
Kiểm tra migration và chạy:
php artisan migrate
Lỗi 4 — Job Failed
Kiểm tra:
php artisan queue:failed
Sau đó retry:
php artisan queue:retry all
Lỗi 5 — Worker dùng cấu hình cũ
Sau khi thay đổi .env hoặc cấu hình Queue, cần chú ý Worker đang chạy có thể vẫn giữ configuration cũ.
Trong quá trình vận hành, Worker thường cần được restart đúng cách sau các thay đổi cấu hình/code.
33. 🧠 Tư duy quan trọng khi học Queue
Đừng học Queue bằng cách nhớ hàng loạt command.
Hãy hiểu dòng chảy:
1. Tạo Job
↓
2. Dispatch Job
↓
3. Queue lưu Job
↓
4. Worker lấy Job
↓
5. handle()
↓
6. Success / Failed
Nếu hiểu được sơ đồ này thì phần lớn Queue đã trở nên dễ hiểu.
Tiếp theo mới học:
Delay
Retry
Priority
Routing
Failed Jobs
Worker
Production
Một cách ghi nhớ khác:
Job
↓
"Việc gì?"
Queue
↓
"Chờ ở đâu?"
Connection
↓
"Lưu bằng gì?"
Routing
↓
"Job này đi đâu?"
Priority
↓
"Xử lý cái nào trước?"
Worker
↓
"Ai xử lý?"
Retry
↓
"Nếu thất bại thì sao?"
34. 📌 Tóm tắt kiến thức & Bài tập thực hành
📌 Tóm tắt
Sau bài này, chúng ta đã biết:
🚀 Queue
Hệ thống hàng đợi để xử lý công việc sau.
📦 Job
Đại diện cho công việc cần thực hiện.
👷 Worker
Process lấy Job ra xử lý.
🗄️ Queue Driver
Quyết định nơi Queue được lưu trữ.
⏳ Delay
Cho phép Job chạy sau một khoảng thời gian.
❌ Failed Jobs
Quản lý những Job thất bại.
🔄 Retry
Cho phép chạy lại Job.
⭐ Priority
Quy định queue nào được Worker ưu tiên.
🧭 Routing
Laravel 13 cho phép định tuyến Job class tới Queue/Connection thông qua:
Queue::route(...)
🧪 Bài tập thực hành
Bài 1 — Tạo Job
Tạo:
php artisan make:job SendPostNotification
Cho Job nhận:
$postId
Bài 2 — Dispatch
Trong Controller:
SendPostNotification::dispatch($post->id);
Bài 3 — Database Queue
Cấu hình:
QUEUE_CONNECTION=database
Đảm bảo database có bảng:
jobs
Bài 4 — Worker
Chạy:
php artisan queue:work
Sau đó tạo một Post và quan sát Worker xử lý Job.
Bài 5 — Failed Job
Cố tình tạo một Exception trong handle().
Sau đó kiểm tra:
php artisan queue:failed
Thử:
php artisan queue:retry all
Bài 6 — Queue Priority
Tạo hai queue:
high
low
Chạy:
php artisan queue:work --queue=high,low
Quan sát thứ tự xử lý.
Bài 7 — Laravel 13 Queue Routing
Tạo một Job:
GeneratePostThumbnail
Sau đó thiết lập route:
Queue::route(
GeneratePostThumbnail::class,
connection: 'redis',
queue: 'images',
);
Sau đó dispatch:
GeneratePostThumbnail::dispatch($post->id);
Hãy tự trả lời:
Job đi đâu?
Connection nào?
Queue nào?
Worker nào xử lý?
Priority có liên quan gì?
🎯 Kết luận
Queue là một trong những cơ chế quan trọng khi xây dựng ứng dụng Laravel thực tế.
Thay vì:
Request
↓
Làm tất cả
↓
Response
chúng ta có:
Request
↓
Lưu dữ liệu
↓
Dispatch Job
↓
Response
↓
Queue
↓
Worker
↓
Job
↓
Processing
Khi kết hợp với:
Event
Listener
Job
Queue
Worker
Laravel có thể xây dựng những hệ thống xử lý công việc phía sau rất linh hoạt.
Và với Laravel 13, chúng ta có thêm:
Queue::route(...)
để định tuyến Job theo class một cách rõ ràng hơn.
Công thức cuối cùng cần nhớ:
JOB
↓
QUEUE
↓
WORKER
↓
HANDLE()
↓
SUCCESS / FAILED
Đây chính là nền tảng để bước sang những kiến trúc Laravel lớn hơn.




Không có nhận xét nào:
Đăng nhận xét