Xử lý bão giao dịch: Queue, Rate Limiting và Circuit Breaker cho hệ thống thanh toán
Hướng dẫn xử lý transaction storm trong flash sale và quyết toán lương với RabbitMQ/Kafka, rate limiting, backpressure, circuit breaker, load shedding, idempotency và capacity planning.
ShanverseUpdated 10 min read— viewsLoading views
Message Queue
Rate Limiting
Resilience
Payment Systems
Bão giao dịch là gì?
Flash sale, ngày phát lương, chốt học phí hoặc đợt quyết toán có thể làm traffic tăng 10–100 lần trong vài giây. Hệ thống thường không sập ở API đầu tiên mà sập theo dây chuyền:
Mục tiêu không phải “xử lý mọi request ngay lập tức”, mà là:
Bảo toàn tiền và trạng thái giao dịch.
Nhận tải trong giới hạn đã cam kết.
Điều tiết công việc theo năng lực downstream.
Từ chối sớm, rõ ràng khi quá tải.
Khôi phục và đối soát được sau sự cố.
Phân loại luồng đồng bộ và bất đồng bộ
Không phải bước nào cũng đẩy vào queue.
Nên đồng bộ
Comments
Xác thực request và quyền.
Kiểm tra idempotency key.
Kiểm tra điều kiện tối thiểu để chấp nhận lệnh.
Ghi durable transaction intent/outbox.
Trả accepted, pending hoặc lỗi rõ ràng.
Có thể bất đồng bộ
Gọi provider khi business flow cho phép pending.
Fraud enrichment không nằm trên critical path.
Gửi email/SMS/push.
Cập nhật analytics, search và reporting.
Reconciliation, retry và compensation.
Kiến trúc chống bão giao dịch
Mermaid
Loading diagram…
Bốn lớp bảo vệ:
Admission control/rate limiting chặn tải thừa trước khi tài nguyên khan hiếm bị chiếm.
Queue làm phẳng burst và tách producer khỏi consumer.
Bounded concurrency/backpressure bảo vệ database và provider.
Circuit breaker dừng gọi dependency đang lỗi/chậm để tránh cascading failure.
Queue-based load leveling
Nếu API nhận 20.000 lệnh/giây trong 30 giây nhưng worker xử lý ổn định 3.000/giây, queue hấp thụ phần chênh lệch. Backlog cực đại gần đúng:
Sau burst, nếu arrival còn 1.000/giây và capacity 3.000/giây:
Capacity plan phải tính message size, replication, disk throughput, retention, retry/DLQ, recovery và peak kéo dài. Queue vô hạn chỉ biến overload thành disk-full chậm hơn.
RabbitMQ hay Kafka?
Chọn theo semantics, không theo độ “hot” của công nghệ. Có thể dùng RabbitMQ cho command/task và Kafka cho immutable payment events, nhưng hai broker cũng tăng vận hành và consistency boundary.
RabbitMQ production checklist
Durable exchange/queue và persistent message khi cần durability.
Quorum queues cho replicated queue phù hợp.
Publisher confirms để biết broker đã nhận message.
Manual consumer acknowledgements; chỉ ack sau khi commit business state.
Bounded prefetch để giới hạn message in-flight.
Dead-letter/exception queue với reason rõ ràng.
Retry có delay, giới hạn lần và jitter; không requeue nóng vô hạn.
Consumer idempotent vì redelivery có thể xảy ra.
Theo dõi ready, unacked, publish/ack rate và memory/disk alarms.
autoAck có thể khiến consumer bị dồn message vào memory và mất message nếu process chết trước khi xử lý.
Kafka production checklist
Chọn partition key theo ordering boundary: paymentId, accountId hoặc merchantId tùy invariants.
Đủ partition để scale nhưng tránh tăng tùy tiện làm thay đổi key mapping.
Producer acknowledgements/durability phù hợp; dùng idempotent producer.
Consumer commit offset sau durable side effect hoặc dùng transactional pattern phù hợp.
Schema Registry và compatible evolution.
Retention đủ cho replay/recovery.
DLQ/parking-lot cho poison event, không skip âm thầm.
Theo dõi consumer lag, rebalance, under-replicated partitions và disk.
Kafka exactly-once không bảo đảm external bank API hoặc database side effect chỉ xảy ra một lần. Vẫn cần idempotency/inbox/outbox.
Transactional Outbox và Inbox
Không dual-write database rồi publish broker như hai bước độc lập. Ghi payment intent và outbox trong cùng transaction:
Publisher đọc outbox và publish retry an toàn. Consumer dùng inbox/unique constraint để tránh xử lý lại:
Nếu insert không tạo row mới, event đã được xử lý.
Idempotency chống retry storm
Client phải gửi idempotency key ổn định cho cùng một ý định thanh toán.
Unique theo merchant/customer + operation + key.
Lưu request fingerprint để phát hiện cùng key nhưng payload khác.
Trả lại kết quả đã lưu cho retry hợp lệ.
Không dùng timestamp ngẫu nhiên làm key khi retry.
Provider call cũng dùng idempotency key nếu hỗ trợ.
Webhook deduplicate theo provider event ID nhưng không gộp hai giao dịch thật.
Timeout không có nghĩa thất bại. Trạng thái an toàn thường là PENDING, sau đó query/reconcile.
Rate limiting và admission control
Rate limit nên nhiều chiều:
Toàn hệ thống theo capacity.
Theo tenant/merchant.
Theo user/account/card/payment method.
Theo endpoint và operation cost.
Theo IP/device như tín hiệu bổ sung, không phải identity duy nhất.
Token bucket cho phép burst nhỏ nhưng giữ average rate. Sliding window hỗ trợ giới hạn chính xác hơn với chi phí state cao hơn.
Khi reject:
429 Too Many Requests cho vượt quota; có Retry-After khi phù hợp.
503 Service Unavailable khi capacity toàn hệ thống cạn.
Error body có request/correlation ID, không chứa dữ liệu nhạy cảm.
Client dùng exponential backoff + jitter và giới hạn retry.
Admission control phải hoạt động trước khi mở DB transaction hoặc chiếm connection quý giá.
Backpressure và bounded concurrency
Worker concurrency phải dựa trên bottleneck:
Autoscale consumer không giới hạn có thể chuyển overload từ queue sang database. Dùng:
Semaphore/concurrency cap.
RabbitMQ prefetch.
Kafka max poll/batch tuning.
Per-provider/per-tenant pool.
Adaptive concurrency dựa trên p99 latency/error.
Bounded internal buffers.
Khi downstream chậm, giảm pull/concurrency trước khi memory phình ra.
Circuit Breaker đúng vai trò
Circuit breaker có ba trạng thái:
Timeout giới hạn thời gian một call.
Retry xử lý lỗi tạm thời.
Circuit breaker dừng gọi dependency đang không khỏe.
Rate limiter giới hạn tốc độ.
Bulkhead cô lập tài nguyên.
Các pattern bổ sung nhau nhưng không thay thế nhau.
Với payment provider, breaker mở không được tự động đánh dấu FAILED nếu kết quả chưa biết. Ghi PENDING_PROVIDER, dừng retry nóng, query trạng thái/reconcile sau đó. Half-open chỉ cho một số probe nhỏ để tránh đánh sập dependency vừa hồi phục.
Retry budget và chống retry amplification
Nếu gateway, service A và worker đều retry 3 lần, một request có thể biến thành hàng chục calls. Chỉ một layer nên sở hữu retry cho một dependency.
Retry chỉ lỗi transient có phân loại.
Exponential backoff với full jitter.
Giới hạn attempt và tổng elapsed time.
Retry budget theo provider.
Tôn trọng Retry-After.
Permanent errors đi thẳng exception queue/manual review.
Không retry validation failure hoặc insufficient funds.
Priority, load shedding và graceful degradation
Tách queue/pool theo mức quan trọng:
Payment authorization/webhook/ledger posting.
Status query và reconciliation.
Notification.
Analytics/report/export.
Không để email backlog chiếm worker của payment. Khi áp lực tăng:
Tắt recommendation và report realtime.
Sampling analytics không quan trọng.
Trả dữ liệu cache stale có kiểm soát.
Hoãn notification không khẩn cấp.
Giảm batch size hoặc concurrency theo bottleneck.
Từ chối sớm traffic ít giá trị hơn theo policy công bằng.
Không load-shed ledger write, webhook bắt buộc hoặc audit evidence một cách âm thầm.
Flash sale và payroll có đặc điểm khác nhau
Flash sale
Burst khó đoán, nhiều user và bot.
Cần virtual waiting room/admission token.
Inventory reservation có TTL và idempotency.
Rate limit theo account/device/IP.
Không oversell chỉ vì queue chậm.
Quyết toán lương
Burst dự đoán được, thường batch lớn.
Validate file trước cửa sổ xử lý.
Chunk batch với stable batch/item IDs.
Stagger schedule theo tenant.
Per-bank/provider throughput budget.
Track progress và partial failure; không rerun toàn bộ batch mù quáng.
Reconciliation theo item và tổng tiền.
Payment state machine
Mermaid
Loading diagram…
State transition phải conditional/atomic. Browser callback không authoritative; provider webhook đã verify hoặc status query/reconciliation mới cập nhật trạng thái.
Ledger/payment database là source of truth. Broker là transport/log, không phải nơi duy nhất chứng minh tiền đã chuyển.
Observability và SLO
Theo dõi đồng thời rate, error, duration và saturation:
Ingress accepted/rejected rate.
Queue depth/age of oldest message.
Kafka consumer lag hoặc RabbitMQ ready/unacked.
Throughput từng worker/provider.
DB pool wait, locks, CPU và IOPS.
Circuit state, open count và half-open result.
Retry rate/budget, DLQ và poison messages.
p50/p95/p99 end-to-end payment latency.
Số giao dịch PENDING quá SLA.
Duplicate suppression và idempotency conflict.
Requested action so với provider/ledger outcome.
Alert theo queue age và SLO, không chỉ queue depth. Một queue 100.000 message có thể ổn nếu mỗi giây xử lý 50.000; queue 500 message có thể nguy hiểm nếu message cũ 30 phút.
Capacity test 100×
Test theo phase:
Baseline ở normal load.
Step load 2×, 5×, 10×.
Spike 100× trong thời gian ngắn.
Soak test ở mức cao kéo dài.
Dependency latency tăng 10×.
Provider trả 429/503/timeouts.
Broker node hoặc consumer restart.
Database failover/connection exhaustion.
Retry storm từ client.
Recovery và backlog drain.
Kiểm tra invariants sau test:
Không double charge.
Không mất accepted command.
Không debit nếu business state không hợp lệ.
Tổng ledger cân bằng.
Không có payment “thành công” chỉ dựa vào enqueue.
Replay không lặp external side effect.
Runbook khi bão tải xảy ra
Bật incident channel và freeze deploy không cần thiết.
Xác định bottleneck bằng p99, queue age và saturation.
Giảm admission limit trước khi dependency sập hoàn toàn.
Mở rộng consumer trong downstream budget.
Tắt/defer workload không quan trọng.
Kiểm tra circuit breaker và retry amplification.
Bảo vệ webhook, ledger và reconciliation lanes.
Theo dõi disk/retention và DLQ.
Không purge queue khi chưa phân tích và phê duyệt.
Đối soát accepted, processed và provider outcomes sau sự cố.
Kết luận
Để sống sót trước traffic 100×, hệ thống thanh toán cần nhận lệnh bền vững, điều tiết tải, giới hạn concurrency và thất bại có kiểm soát. Queue hấp thụ burst; rate limiter bảo vệ cửa vào; backpressure bảo vệ downstream; circuit breaker chặn cascading failure; idempotency, outbox/inbox và reconciliation bảo vệ tính đúng của tiền.
Scale tốt không phải là xử lý vô hạn, mà là biết chính xác hệ thống có thể nhận bao nhiêu, xếp hàng bao lâu và từ chối thế nào mà không mất hoặc trừ tiền hai lần.
Table of contents(24)
Codeplain text
1API latency tăng2→ client timeout và retry3→ connection pool đầy4→ database lock/CPU tăng5→ payment provider bị gọi dồn6→ retry storm7→ toàn hệ thống mất khả năng phục vụ
Hướng dẫn kiến trúc 2FA cho Mobile Banking: SMS OTP, TOTP/Smart OTP, FIDO2, WebAuthn, passkeys, sinh trắc học cục bộ, đăng ký thiết bị, step-up và khôi phục tài khoản.
Hướng dẫn tích hợp cổng thanh toán VNPay, ZaloPay, MoMo và Stripe: payment flow, chữ ký webhook/IPN, idempotency, retry, reconciliation, refund và vận hành production.