→ From Coder to Shipper
From Coder to Shipper · Phần thêm

Message Queue — hàng đợi tin nhắn

Lớp tách rời 'ai GỬI việc' khỏi 'ai LÀM việc' — một hàng tin nhắn nơi producer bỏ nhiệm vụ vào và consumer lấy ra khi sẵn sàng.

← / → để chuyển slide · chuyển EN/VI bên dưới

Nền tảng

Message queue là gì?

Một hàng đợi first-in-first-out gồm các tin nhắn nhỏ, đứng giữa phần app TẠO việc và phần app LÀM việc.

Một hàng tin nhắn

Các tin nhắn (thường là chuỗi JSON nhỏ) xếp hàng: vào trước, ra trước. Hàng đợi chỉ là nơi lưu — một hàng chờ.

Producer → hàng → consumer

Producer công bố (PUBLISH) tin nhắn; consumer tiêu thụ (CONSUME) sau đó. Chúng không bao giờ gọi thẳng nhau.

Tách rời

Người gửi không cần biết ai xử lý, lúc nào, và có bao nhiêu người xử lý.

Đệm tải

Công việc có thể chất đống trong hàng khi consumer bắt kịp — bộ giảm xóc cho hệ thống.

Sự khác biệt

Một request, hai kiểu

Cùng khoảnh khắc 'user đặt hàng', có và không có hàng đợi ở giữa.

Không có hàng (đồng bộ)

User bấm 'đặt hàng' → app gửi email, gọi API thanh toán, render hóa đơn — tất cả trong cùng một request. User nhìn spinner 8 giây.

Chậm — và nếu API thanh toán hiccup, cả request hỏng.

Có hàng (bất đồng bộ)

User bấm 'đặt hàng' → app bỏ MỘT tin nhắn vào hàng, trả lời trong 50 ms, xong. Worker xử lý email/thanh toán/hóa đơn chạy nền.

Trả lời nhanh — và worker chậm không chặn ai cả.

Nó làm gì

Message queue làm bốn việc

Tách rời các phần

Web app, worker và API bên thứ ba nói chuyện qua hàng — không phải gọi thẳng nhau.

Hấp thụ đỉnh tải

1.000 đơn trong một phút? Chúng xếp hàng; worker gặm dần theo tốc độ của mình.

Biến việc thành bất đồng bộ

Email, báo cáo, render video — mọi thứ không cần xong trước khi user thấy 'hoàn tất'.

Thêm độ tin cậy

Tin nhắn được lưu bền trong hàng: worker sập giữa chừng, tin vẫn còn để giao lại.

Vì sao

Vì sao nên dùng hàng đợi

Ba lý do, đều xoay quanh chuyện không chờ và không mất.

Đừng chặn request

Thời gian trả lời không còn phụ thuộc vào tác vụ chậm nhất. Tám giây việc trở thành 50 ms xếp hàng.

Độ tin cậy

Worker hỏng không đồng nghĩa mất tin — thử lại, hoặc gửi vào dead-letter queue để kiểm tra sau.

Scale độc lập

Traffic tăng gấp đôi? Thêm một consumer. Hàng là mối nối cao su giữa nhu cầu và năng lực.

01
Phần 1

Nó hoạt động thế nào (bản nhẹ)

publish → lưu → phân phối → tiêu thụ → ack. Năm từ, một vòng lặp.

Luồng xử lý

Cuộc đời của một tin nhắn

Theo một đơn hàng từ lúc bấm cho đến khi xong.

  1. 1

    Producer công bố

    App của bạn gọi publish() kèm tin nhắn — ví dụ JSON của một đơn hàng với id 42. Đó là toàn bộ phía gửi.

  2. 2

    Broker lưu tin

    Server hàng đợi (broker) nhận tin và giữ lại — trong bộ nhớ và/hoặc trên disk, tùy hệ thống.

  3. 3

    Phân phối quyết định đi đâu

    Tin vào hàng nào? Hệ đơn giản: một hàng mỗi loại việc. Hệ lớn hơn phân phối theo tên hoặc topic.

  4. 4

    Một consumer tiêu thụ

    Worker lấy tin kế tiếp và làm việc: gửi email, gọi API, render hóa đơn.

  5. 5

    Ack — hoặc thử lại

    Xong việc, consumer xác nhận (ack). Hỏng giữa chừng? Tin được giao lại — nguyên tắc 'ít nhất một lần'.

Bức tranh

Ba khối, một hàng ở giữa

Không ai gọi ai. Mọi thứ là tin nhắn đi dọc theo hàng.

📤 Producer (web app của bạn)

Flask · FastAPI · any app

Tạo ra việc và bỏ xuống hàng: publish(order). Không chờ, không biết ai sẽ lấy.

⟶

publish()

📥 Broker — hàng đợi

RabbitMQ · Redis · Kafka

Giữ hàng: lưu tin, phân phối vào hàng đúng, giao lại nếu consumer chết. Bộ giảm xóc.

⟶

consume() + ack

🔧 Consumers (worker)

worker-1 · worker-2 · worker-3 …

Vòng lặp mãi: lấy tin kế tiếp, làm việc, ack. Bận quá? Thêm worker — hàng không bận tâm.

Producer và worker chỉ gặp hàng đợi — không bao giờ gặp nhau.

Hai kiểu

Queue so với topic

Một tin — một consumer, hay nhiều? Đó là ngã ba.

Queue — điểm-điểm

Một tin, một consumer. Dòng việc: worker nào rảnh sẽ lấy nhiệm vụ kế. Tải được chia đều tự động.

Dùng cho NHIỆM VỤ: 'xử lý đơn này'.

Topic — phát sóng

Một tin, nhiều consumer. Mỗi người đăng ký nhận bản sao riêng — kiểu pub/sub.

Dùng cho SỰ KIỆN: 'một đơn đã được đặt'.

Từ vựng

Sáu từ, một hơi

Tin nhắn

Nội dung — thường là chuỗi JSON kèm metadata (id, loại, thời gian). Nhỏ và trọn vẹn.

Broker

Server hàng đợi lưu và phân phối tin: RabbitMQ, Kafka — hoặc Redis thay thế.

Producer / consumer

Hai vai trò: ai bỏ tin vào, ai lấy ra. Cùng một app có thể làm cả hai.

Ack

Lời 'xong rồi, bỏ đi' của consumer. Chưa có ack, hàng vẫn giữ tin.

Thử lại / DLQ

Tin hỏng được thử lại, rồi để vào dead-letter queue — bãi đỗ cho việc hỏng.

Backpressure

Hàng đầy khi producer nhanh hơn consumer — tín hiệu rõ ràng, chính xác để tăng năng lực.

Ví dụ 1 · producer

Publish: bỏ một tin vào hàng

Toàn bộ chiêu 'bất đồng bộ' phía web app: một dòng, request rảnh ngay.

producer.py
import redis

r = redis.Redis(host="localhost", port=6379)

def publish(order):
    # put one message onto the queue
    r.lpush("orders", order)
    print("queued:", order)

publish({"id": 42, "item": "keyboard", "qty": 1})

lpush = đẩy vào đầu danh sách: tin nằm ở đầu list 'orders' — một hàng đợi bằng một lệnh Redis.

Ví dụ 2 · consumer

Consume: vòng lặp làm việc cho đến khi hàng vắng

Phía worker: lấy tin kế tiếp, làm việc, lặp lại. Không gì mất cả.

worker.py
import redis, json

r = redis.Redis(host="localhost", port=6379)

while True:
    item = r.brpop("orders", timeout=5)
    if item is None:
        continue            # queue empty — keep waiting
    order = json.loads(item[1])
    print("processing:", order)
    # work: send email, call API, render PDF…
    # then loop — the next message is up

brpop = lấy ra có chặn: worker ngủ đến khi có tin (timeout = ngủ bao lâu).

Trong terminal

Xem hàng đợi từ terminal

LLEN = đang chờ bao nhiêu; LRANGE = liếc xem bên trong; LPOP = lấy một ra — đó là một lần consume.

ops@vps — zsh
$ redis-cli LLEN orders
3
$ redis-cli LRANGE orders 0 -1
1) {"id": 42, "item": "keyboard", "qty": 1}
2) {"id": 43, "item": "mouse", "qty": 2}
$ redis-cli LPOP orders
{"id": 42, "item": "keyboard", "qty": 1}
← LPOP = consume: read one message and remove it
Bức tranh

Những tên bạn sẽ thực sự gặp

Ba cái này đủ cho gần như mọi hệ thống nhỏ-vừa bạn sẽ ship.

RabbitMQ

Broker đa năng kinh điển: hàng, phân phối, ack, thử lại. Khi ai đó nói 'message queue', thường là họ đang nói đến cái này.

Redis

Bạn đã biết nó với vai trò cache — list của nó kiêm được hàng đơn giản (LPUSH/BRPOP). Hợp cho hệ nhỏ; không sinh ra để đảm bảo chặt chẽ.

Kafka

Gã khổng lồ event log: chỉ thêm, chia partition, phát lại được. Chạy các luồng sự kiện khối lượng lớn — một loài khác (lớn hơn).

Cạnh nhau

RabbitMQ so với Redis so với Kafka

RabbitMQRedisKafka
Nó thực sự là gìBroker tin nhắn (hàng + phân phối)Cache kiêm hàng đơn giảnNhật ký sự kiện phân tán
Đảm bảoack + thử lại, vữngcơ bản (mặc định trong bộ nhớ)log bền, phát lại được
Hợp nhất choViệc ứng dụng chung: job, tách rờiHệ nhỏ, async nhanhLuồng sự kiện khối lượng lớn
Ngoài phạm vi

Những gì cố tình bỏ qua

Hàng đợi đi rất sâu. Những thứ này là thật — nhưng không phải bước tiếp theo của bạn.

Exchange & routing key

Phân phối tinh vi của RabbitMQ (direct, fanout, topic). Thừa thãi khi bạn chưa có nhiều hàng và rule.

Partition & consumer group

Cấu trúc nội bộ Kafka: partition, offset, consumer group. Mạnh — đủ một khóa học riêng.

Xử lý luồng

Biến đổi luồng tại chỗ (Kafka Streams, Flink). Đây là vùng tiên phong — không phải bước tiếp theo của bạn.

Message queue là một mối nối cao su trong hệ thống: người gửi bỏ việc xuống hàng và đi tiếp; worker lấy theo tốc độ của mình; và không gì mất khi có ai đó hiccup.
toàn cảnh

Tách rời. Đệm. Thử lại.

Tiếp theo: khi một máy chưa đủ, hàng đợi là thứ đầu tiên bạn nghĩ đến.