User bấm “Gửi Email”. API của bạn đứng hình 3 giây trong khi nó đang trao đổi với SMTP server. Rồi mới trả về “OK.”
Cái 3 giây chờ đó là tội ác.
Mọi thao tác không cần xảy ra ngay bây giờ thì nên xảy ra sau đó, trong nền. Đây là toàn bộ triết lý đằng sau task queue và message broker.
Đây là Hướng dẫn chuyên sâu về xử lý bất đồng bộ — từ cái task queue Django đơn giản nhất đến Kafka quy mô hành tinh.
Phần 1: Nền tảng (Mô hình tư duy)
Ý tưởng cốt lõi rất đơn giản: Tách “Yêu cầu” khỏi “Công việc”.
| |
Phân cấp “Bưu điện”
Hãy tưởng tượng như ba tầng của hệ thống thư tín:
Task Queue (Celery / Django-Q) = Hòm thư trước cửa nhà Đơn giản, nội địa. Bạn nhét thư vào, người đưa thư tự đến lấy. Không cần quan tâm nó đi đâu.
Message Broker (RabbitMQ) = Chi nhánh Bưu điện Định tuyến thông minh. Bạn mang một gói hàng “KHẨN” và một gói “Thường”. Nhân viên biết gửi Khẩn theo xe chuyển phát nhanh và Thường theo xe hàng tuần. Nhiều người gửi, nhiều người nhận.
Event Streaming (Kafka) = Tờ báo Quốc gia Mọi người đăng bài (events). Mọi người đăng ký đọc theo tốc độ riêng. Bài cũ được lưu trữ mãi. Bạn có thể đọc lại (replay) tờ báo thứ Ba tuần trước bất cứ lúc nào.
Phần 2: Bộ công cụ (Ai làm gì?)
Hiểu các tầng là cực kỳ quan trọng. Chúng giải quyết những vấn đề khác nhau.
Tầng 1: Task Queue (Celery / Django-Q)
Đây là điểm khởi đầu. Bạn có một app Django, cần chạy thứ gì đó ở nền.
Là gì: Một thư viện Python chạy hàm bất đồng bộ trong một process riêng (worker).
Cần gì: Một Broker để lưu trữ các task. (Mặc định dùng Redis hoặc RabbitMQ).
| |
Dùng Celery khi: Gửi email, resize ảnh, tạo PDF, chạy import hàng đêm.
Tầng 2: Message Broker (RabbitMQ)
Đây là phần “trung gian”. Nó là một server chuyên dụng để nhận, lưu trữ và định tuyến messages.
Khái niệm chính:
- Producer: Gửi messages vào broker.
- Queue: Một bộ đệm (buffer) có tên, lưu trữ messages.
- Consumer: Lấy messages từ queue để xử lý.
- Exchange: Bộ “Router”. Dựa trên routing key, nó quyết định message đi vào queue nào.
Dùng RabbitMQ khi: Có nhiều service cần nói chuyện với nhau bất đồng bộ. “Khi có Đơn hàng mới, thông báo cho Service Kho hàng VÀ Service Thông báo.”
Tầng 3: Event Streaming Platform (Kafka)
Kafka là một con thú hoàn toàn khác. Nó không phải là queue — nó là một log phân tán, chỉ ghi thêm (append-only).
Khái niệm chính:
- Topic: Như một bảng trong database, hoặc một kênh tin tức.
- Partition: Một topic được chia thành nhiều partition để tăng tốc xử lý song song.
- Offset: Số thứ tự tuần tự của mỗi message. Consumer X đang ở offset 1500, Consumer Y ở offset 2300. Messages không bao giờ bị xóa (cho đến khi hết thời gian lưu trữ).
- Consumer Group: Nhiều consumer cùng xử lý song song một topic. Kafka chia partition cho từng người.
Dùng Kafka khi: Bạn cần nhật ký kiểm toán (audit log) vĩnh viễn, có thể phát lại. “Mọi sự kiện thanh toán, mãi mãi. Bất kỳ service nào cũng có thể đăng ký đọc ngay bây giờ hoặc bắt kịp từ 3 tháng trước.”
Phần 3: Điều tra (Debug như chuyên gia)
1. Theo dõi Celery Workers
| |
2. Dead Letter Queue (DLQ) — Hàng đợi xác chết
Pattern quan trọng nhất bạn phải implement. Nếu một task thất bại vĩnh viễn (thử lại 3 lần, vẫn lỗi), nó đi đâu?
- Không có DLQ: Message bị nuốt vào hư không. 🔥 Mất dữ liệu.
- Có DLQ: Message thất bại được chuyển vào một queue đặc biệt “Xác chết”. Bạn có thể soi nó, fix bug, rồi đẩy lại.
Trong RabbitMQ: Cấu hình qua x-dead-letter-exchange.
Trong Celery: Dùng hook on_failure hoặc xử lý DLQ ở cấp task.
3. Consumer Lag trong Kafka
Chỉ số quan trọng nhất trong Kafka. Consumer Lag = Consumer đang trễ bao nhiêu message so với message mới nhất.
| |
Nếu lag tăng, consumer của bạn không xử lý kịp. Cần thêm instance consumer (tối đa bằng số partition).
Phần 4: Chẩn đoán (Lỗi thường gặp)
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
| Queue cứ phình to mãi | Worker quá chậm | Thêm Celery worker. Tối ưu task. |
| Cùng một task chạy hai lần | Không có idempotency. Worker crash giữa chừng, broker thử lại. | Làm task idempotent: “Nếu đơn hàng #123 đã xử lý rồi thì bỏ qua.” |
| Django-Q vs Celery | Django-Q không cần broker riêng (dùng DB). Celery mạnh hơn nhưng cần Redis/RabbitMQ. | Dùng Django-Q cho dự án nhỏ. Dùng Celery cho production. |
| Kafka mất message | acks=1 nghĩa là chỉ leader xác nhận. Leader crash trước khi replica kịp sao chép. | Set acks=all ở Producer. |
| Kafka không scale được | Topic chỉ có 1 partition. Không thể có nhiều consumer hơn số partition. | Tăng số partition khi tạo Topic. (Không giảm được sau khi tạo!) |
Phần 5: Giải pháp (Sách nấu ăn Python)
1. Celery với Django (Bộ chuẩn)
| |
2. Django-Q (Không cần hạ tầng)
Khi bạn không muốn setup Redis/RabbitMQ cho dự án nhỏ.
| |
3. Kafka với Python (Luồng sự kiện)
| |
Mô hình tư duy chốt hạ
| |
Hướng dẫn chọn lựa:
- App Django nhỏ, chạy ngầm? →
Django-Q(không cần hạ tầng thêm). - App Django production? →
Celery + Redis. - Nhiều microservice cần nói chuyện? →
RabbitMQ. - Event sourcing, audit log, pipeline dữ liệu liên team? →
Kafka.
