How we built a queue on top of Kafka
Bài tóm tắt Monzo 2024 3. Quy mô lớn Độ khó: Trung bình
Monzo · 2024 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc
Bối cảnh
- Messaging bất đồng bộ có trong kiến trúc microservices của Monzo từ đầu. Ban đầu dùng NSQ: tiện kiểu queue (xử lý và ack từng message), nhưng không bền, đó là khoảng cách duy nhất tới at-least-once.
- Chuyển sang Kafka vì độ bền, nhưng muốn giữ ngữ nghĩa queue. Biến một distributed log thành queue không phải việc muốn mỗi kỹ sư tự làm, nên họ dồn vào một client library. Lúc viết bài (2024): hơn 400 service dùng thư viện này để subscribe hơn 2.500 topic.
Thay đổi chính
Ý tưởng cốt lõi: Monzo có rất ít chỗ thật sự cần thứ tự, nên họ bỏ yêu cầu thứ tự để có hai thứ.
- Dead letter chống poison pill: Kafka thuần xử lý tuần tự trong partition, một message không xử lý được sẽ chặn cả partition tới khi có bản vá. Thư viện thử
maxAttemptslần rồi chuyển message sang topic deadletter riêng cho mỗi cặp consumer group + topic, báo người, rồi đi tiếp. Muốn chạy lại thì CLI đẩy message sang topic retry riêng của consumer group đó (không publish lại topic gốc, vì sẽ giao lại cho mọi consumer group khác); muốn bỏ thì CLI chỉ commit offset trên deadletter. - Xử lý đồng thời trong một partition: goroutine xử lý song song nhiều message. Cái khó là commit offset: không được commit offset 3 khi 2 chưa xong, nếu không restart sẽ bỏ qua 2 vĩnh viễn.
UnorderedOffsetManagertheo dõi các message đang bay và commit tới watermark liên tục đã xong.- Chỉ cho bật kiểu này khi đã có deadletter, để offset chắc chắn tiến sau
maxAttempts. - "Max runaway": giới hạn khoảng cách giữa offset đã commit thấp nhất và offset sắp lấy, tức trần cho lượng việc trùng phải làm lại sau restart.
- Lợi ích: cần ít partition hơn, và khi tồn đọng thì kỹ sư chỉ cần tăng concurrency thay vì đoán lại số partition từ đầu.
- Chỉ cho bật kiểu này khi đã có deadletter, để offset chắc chắn tiến sau
- Interface fluent có kiểu: tổ hợp tuỳ chọn không hợp lệ thì lỗi biên dịch (bỏ
WithDeadletterthì không gọi đượcWithUnorderedConcurrency). Bỏ cả hai thì có subscription Kafka thuần, tuần tự. - Đang làm thêm: khoá phân tán trên partition, khoá cục bộ theo partition key để lấy lại thứ tự trong từng key mà vẫn chạy song song giữa các key.
Vì sao (theo Monzo)
Triết lý "có chính kiến" của Monzo: làm con đường phổ biến nhất trở nên dễ và dễ chịu. Thư viện là chỗ thể hiện điều đó.
Bài học cho SWE
- Hỏi thẳng "có thật cần thứ tự không?"; bỏ được thứ tự thì có dead letter và song song trong partition (Nền tảng nội bộ cho service (đường ray chung)).
- Commit offset khi xử lý không theo thứ tự cần watermark và một trần cho việc làm lại.
- Đưa ràng buộc an toàn vào hệ kiểu của thư viện để cấu hình sai không lọt tới production.
- Dead letter và retry nên tách theo consumer group, vì mỗi group có lỗi riêng.
Liên quan
Được nhắc tới trong
Công ty (1)
Bài tóm tắt (3)