Series Docker & Kafka cho Laravel #3: Vượt bão API sập với Dead Letter Queue (DLQ) (Trạm cuối)
1. Tử huyệt "Poison Pill" và Sự cố kẹt đường ống
Ở Bài 2, chúng ta đã tắt tính năng Auto Commit. Nghĩa là nếu API của phía Hitachi sập, lệnh Http::post() trong PHP sẽ ném ra Exception. Consumer lúc này sẽ báo với Kafka: "Tôi xử lý thất bại rồi, đừng lưu Offset (vị trí đọc) vội".
Hậu quả là ngay ở vòng lặp mili-giây tiếp theo, Kafka lại ném chính cái thông điệp đó cho Consumer xử lý lại. Consumer lại gọi API, lại lỗi, lại ném Exception. Tiến trình này lặp lại hàng vạn lần trong 1 phút, làm cháy CPU, và quan trọng nhất: Hàng ngàn giao dịch quẹt thẻ hợp lệ của hành khách xếp hàng phía sau sẽ bị kẹt cứng lại, không thể tiến lên được.
Thông điệp gây kẹt hệ thống này trong thế giới Event-Driven gọi là Poison Pill (Viên thuốc độc).
2. Dead Letter Queue (DLQ) là gì?
Thay vì cố chấp đâm đầu vào tường, kiến trúc DLQ định ra một luật chơi thông minh hơn: "Nếu xử lý thất bại quá 3 lần, hãy vứt viên thuốc độc đó sang một thùng rác riêng biệt (DLQ Topic), bỏ qua nó và đi tiếp để xử lý các giao dịch phía sau".
Nhờ vậy, đường ống chính (metro.transactions) luôn được thông suốt. Các giao dịch lỗi sẽ nằm ngoan ngoãn ở thùng rác (metro.transactions.dlq) chờ chúng ta dùng các tool khác (hoặc code script) lôi ra xử lý tay vào cuối ngày.
3. Cấu hình DLQ bằng Laravel Kafka
Package mateusjunges/laravel-kafka mà chúng ta cài ở Bài 1 đã tích hợp sẵn cơ chế này cực kỳ thanh lịch. Bạn chỉ cần điều chỉnh lại File Command khởi chạy Consumer một chút.
Mở lại file StartTransactionConsumer.php và cập nhật đoạn build Consumer:
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Junges\Kafka\Facades\Kafka;
use App\KafkaHandlers\TransactionSyncHandler;
class StartTransactionConsumer extends Command
{
protected $signature = 'kafka:consume-transactions';
protected $description = 'Chạy Worker hứng sự kiện giao dịch vé có bảo vệ DLQ';
public function handle()
{
$this->info("Bắt đầu lắng nghe với khiên DLQ...");
$consumer = Kafka::createConsumer(['metro.transactions'])
->withConsumerGroupId('hitachi_sync_group')
->withHandler(new TransactionSyncHandler())
->withAutoCommit(false)
// 🛡️ BẬT TÍNH NĂNG DEAD LETTER QUEUE
// Nếu lỗi, ném message vào topic 'metro.transactions.dlq'
->withDlq('metro.transactions.dlq')
// Cho phép thử lại tối đa 3 lần trước khi ném vào DLQ (cách nhau theo cơ chế backoff nếu cần)
->withMaxRetries(3)
->withMaxMessages(1000) // Vẫn giữ chốt chặn chống tràn RAM
->build();
$consumer->consume();
}
}
Với đúng 2 dòng code cấu hình thêm (withDlq và withMaxRetries), hệ thống của bạn đã sở hữu khả năng tự động cách ly lỗi. PHP không còn bị treo, và Kafka cứ thế cuốn trôi dòng sự kiện một cách êm ái.
4. Tái xử lý và Thu thập bằng chứng
Hệ thống của đối tác báo đã khắc phục xong mạng lúc 3 giờ chiều. Bây giờ bạn cần phải đồng bộ lại toàn bộ những giao dịch đã bị đẩy vào DLQ từ sáng đến giờ.
Rất đơn giản, DLQ bản chất cũng chỉ là một Topic bình thường của Kafka.
Bạn chỉ cần viết một Command khác (ví dụ: php artisan kafka:retry-dlq), tạo một Consumer mới trỏ thẳng vào Topic metro.transactions.dlq. Lần này, nó sẽ đọc các thông điệp lỗi ra, bóc tách card_uid và JSON payload nguyên thủy, rồi thử gửi lại API một lần nữa.
Đồng thời, bạn có thể nối một nhánh dữ liệu từ Topic DLQ này chạy thẳng vào Elasticsearch. Khi mở Dashboard Kibana lên, sếp và team vận hành sẽ nhìn thấy ngay một biểu đồ cột báo đỏ: "Từ 8h00 đến 10h00, có chính xác 4.215 giao dịch đồng bộ thất bại". Bạn có thể export danh sách này ra file CSV chỉ với 1 cú click để làm hồ sơ đối soát kỹ thuật.
Tổng kết Series Docker & Kafka
Trải qua 3 bài học cường độ cao, bạn đã trang bị cho mình một tư duy thiết kế hệ thống phân tán hoàn chỉnh:
- Loại bỏ sự cồng kềnh bằng Kafka KRaft Mode và cấy lõi C-extension vào PHP để tối ưu tốc độ.
- Hiểu được nguyên lý Consumer Group, dùng Kafka Key để giữ đúng thứ tự dữ liệu quẹt thẻ, và chiến lược chém tiến trình để diệt trừ triệt để Memory Leak trong Laravel.
- Hoàn thiện độ tin cậy của hệ thống bằng lá chắn Dead Letter Queue (DLQ), đảm bảo đường ống giao dịch không bao giờ bị nghẽn mạch kể cả khi các hệ thống vệ tinh sụp đổ.
Với bộ khung kiến trúc Microservices & Event-Driven này (kết hợp với Nginx, Redis, ELK, và Master-Slave Database đã dựng trước đây), bạn đã nắm trong tay mô hình lõi của mọi công ty công nghệ lớn nhất hiện nay.
All rights reserved