任務隊列和平衡
1)為什麼要排隊任務
任務隊列(job queue/work queue)會按時間和速度將制造商和表演者分開:- 平滑峰:前部和重子系統之間的緩沖區。
- 穩定SLA:負載類的優先級和隔離。
- 簡化容錯能力:retrais, DLQ,重新生產。
- 橫向縮放:添加操作員而不更改API。
類型域:支付處理、符號化、報告/媒體生成、ETL/ML後處理、與外部API集成。
2)模型和基本概念
生產者:發布任務(payload+元數據:idempotency key,優先級,截止日期)。
隊列/拓撲:緩沖區/任務日誌。
Worker:獲取任務、處理、確認(ack)或返回錯誤。
Visibility Timeout/Lease:在處理期間的「租賃」任務,之後-自動重新交付。
DLQ (Dead Letter Queue):在嘗試/致命錯誤限制後,「埋葬」任務。
Rate Limit/Concurrency:對每個管理者/每個隊列/每個隊列的消費限制。
- Pull:用戶自己請求任務(分配負載)。
- 推動:經紀人大炮;需要保護以防止弱者的「填充」。
3)交付和確認語義
最多:沒有撤退;更快,但損失是可能的。
On-Least-once(大多數隊列的默認值):重復是可能的→需要處理程序的等效性。
Effectively exactly-once:在應用程序級別實現(等效性、滯後源、交易/outbock)。經紀人可以提供幫助,但不是「魔術子彈」。
- Ack/Nack:明確的結果。
- Requeue/Retry: с backoff + jitter.
- 毒藥消息:發送到DLQ。
4)平衡與規劃
4.1優先級和算法
FIFO:簡單而可預測。
優先級:優先級(P0…… P3)。
WRR/WSR(Weighted Round-Robin/Random):類之間的CPU/隔板份額。
WFQ/DRR(類似於網絡中的「公平」隊列):按頻率/客戶份額。
截止日期/EDF:用於截止日期任務。
公平分享:限制「嘈雜的鄰居」(點對點)。
4.2處理流
單次飛行/Coalescing:將重復的任務組合到密鑰上。
Concurrency caps:按任務/集成類型(外部API)嚴格限制並行性。
4.3 Geo和Sharding
按鍵(tenant/id)的節拍器→數據的位置,節拍器內的穩定順序。
粘貼緩存/資源:哈希路由到具有「附加」狀態的竊賊。
5)Retrai,backoff和DLQ
指數backoff+jitter:「base 2^attempt ± random」。
每個任務的最大嘗試次數和共享截止日期(時間到日期)。
錯誤分類:「retryable」(網絡/限制),「non-retryable」(驗證/業務禁令)。
Parking/Delay Queue:延遲的任務(例如,在15分鐘後重復)。
DLQ策略:確保指定「有毒」消息到達何處和條件;預設重新啟動器。
6)相似性和重復數據消除
任務中的Idempotency-Key;用於最新N鍵的TTL插槽(Redis/DB):- seen → skip/merge/result-cache.
- 自然鍵:使用'order_id/ payment_id'代替隨機UUID。
- Outbox:將任務的事實及其狀態記錄在一個具有業務操作的DB事務中。
- 真人秀:「UPSERT」按鍵,版本驗證,隊列中的「at least-once」+DB的冪等。
7)多重性和SLA課程
按類別劃分隊列/流:「critical」、「standard」、「bulk」。
配額和優先級(黃金/銀/青銅)。
隔離:P0下的熟練人員;背景-在單獨的群集/nods中。
Admission Control:不接受超出截止日期處理的範圍。
8)Autoskeyling workers
滑板的度量標準:queue depth、arrival rate、processing time, SLA截止日期。
KEDA/Horizontal Pod Autoscaler: SQS/Rabbit/Kafka lag深度觸發器。
限制因素:外部API限制,數據庫(不破壞後端)。
9)技術選擇和模式
9.1 RabbitMQ/AMQP
Exchanges: direct/topic/fanout;Queues с ack/ttl/DLQ (dead-letter exchange).
Prefetch(QoS)調節「竊賊上多少個任務」。
DLX示例:ini x-dead-letter-exchange=dlx x-dead-letter-routing-key=jobs.failed x-message-ttl=60000
9.2 SQS(和類似物)
Visibility Timeout, DelaySeconds, RedrivePolicy (DLQ).
相等性-在應用程序上(去勢表)。
限制:batchi 1-10條消息;專註於同等的辛基。
9.3 Kafka/NATS JetStream
對於大型管線:高帶寬,重建/中繼。
登錄頂部的任務隊列:一個任務=一個消息;通過分期/主題控制「每個密鑰一個用戶」。
Retrai:帶有backoff的單個拓撲/主題後綴。
9.4 Redis隊列(Sidekiq/Resque/Bull/Celery-Redis)
非常低的潛伏期;保持彈性(RDB/AOF),回歸密鑰和單飛鎖鍵。
適合「輕量級」任務,不適合長期恢復。
9.5個框架
Celery(Python),Sidekiq(Ruby),RQ/BullMQ(Node),Huey/Resque是現成的復古器,時間表,中間件和度量標準。
10)路由和平衡方案
Round-Robin:均勻,但不考慮任務的「嚴重性」。
重量RR:按生產者/池容量分配。
Fair/Backpressure-aware:只有準備就緒時,用戶才能完成新任務。
優先排隊:每個班級分別排隊;如果有,workers會按順序閱讀[P0→…… →Pn]。
Hash-routing: 「hash (key)% shards」-用於靜態/緩存處理。
11)泰茅斯,截止日期和SLA
按任務計時:內部的「租賃」工作(在采購者代碼中)≤經紀人的Visibility Timeout。
Global deadline: T後挑戰毫無意義-NACK→DLQ。
Budget-aware:在臨近截止日期(部分結果)時縮短工作(brownout)。
12)可觀察性和控制
12.1個指標
`queue_depth`, `arrival_rate`, `service_rate`, `lag` (Kafka), `invisible_messages` (SQS).
`success/failed/retired_total`, `retry_attempts`, `dlq_in_total`, `processing_time_ms{p50,p95,p99}`.
`idempotency_hit_rate`, `dedup_drops_total`, `poison_total`.
12.2 Logi/Tracing
相關:「job_id」,「correlation_id」,重復數據消除密鑰。
慶祝「retry/backoff/dlq」作為事件;帶有源請求的span的鏡片。
12.3 Dashbords/Alerts
觸發器:深度>X, p99> SLO, DLQ增長,「紮根」任務(可見性>N),熱鍵。
13)安全性和合規性
租戶隔離:單獨的隊列/密鑰空間、ACL、配額。
運輸和/或「靜止」加密。
付費中的PII最小化;哈希/ID代替原始PII。
秘密:不要將令牌聚集到任務主體中,使用vault/refs。
14)反模式
Retrai無止境→交易/金錢「兩次」。
一個巨大的隊列「全力以赴」→沒有隔離,不可預測的延誤。
沒有DLQ的無限回程→永恒的「有毒」任務。
Visibility Timeout<處理時間→級聯重復。
隊列中的大型付費→壓入網絡/內存;更好地存儲在對象堆棧中並傳輸鏈接。
沒有後壓的push模型→竊賊被淹沒。
在一個騙子池中混合關鍵任務和散熱任務。
15)實施支票
- 將任務分類為SLA (P0/P1/P2)和範圍。
- 選擇具有所需語義和語義的經紀人/框架。
- 設計密鑰、優先級和路由(hash/shards/priority lanes)。
- 啟用帶有backoff+ jitter和DLQ策略的轉發。
- 通過TTL實現等效性(鍵,upsert, dedup stor)。
- 配置時間:按任務、可見性、共享截止日期。
- 限制積分和等級。
- 帶保險絲的深度/滯後自動滑行。
- 度量/tracing/alerts;runbooks在「風暴」和DLQ溢出。
- fails測試:竊賊摔倒,「有毒」信息,超載,長任務。
16)配置和代碼示例
16.1 Celery (Redis/Rabbit)-基本水流
python app = Celery("jobs", broker="amqp://...", backend="redis://...")
app.conf.task_acks_late = True # ack после выполнения app.conf.broker_transport_options = {"visibility_timeout": 3600}
app.conf.task_default_retry_delay = 5 app.conf.task_time_limit = 300 # hard timeout
@app.task(bind=True, autoretry_for=(Exception,), retry_backoff=True, retry_jitter=True, max_retries=6)
def process_order(self, order_id):
if seen(order_id): return "ok" # идемпотентность do_work(order_id)
mark_seen(order_id)
return "ok"
16.2 RabbitMQ — DLQ/TTL
ini x-dead-letter-exchange=dlx x-dead-letter-routing-key=jobs.dlq x-message-ttl=600000 # 10 минут x-max-priority=10
16.3 Kafka-按級別分列的retrai
orders -> orders.retry.5s -> orders.retry.1m -> orders.dlq
(通過scheduler/cron-consumer轉移延遲交付。)
16.4 NATS JetStream — consumer с backoff
bash nats consumer add JOBS WORKERS --filter "jobs.email" \
--deliver pull --ack explicit --max-deliver 6 \
--backoff "1s,5s,30s,2m,5m"
17) FAQ
Q: 什麼時候選擇推拉?
答:拉出自然的後壓和「誠實」的平衡;在低速和需要最低TTFB時更容易推動,但需要限制器。
Q: 如何避免熱鍵?
答:通過復合鍵('order_id% N')進行擠壓,緩沖並進行擊球處理,輸入按鍵限制。
Q:「exactly-once」可以嗎?
答:實際上-通過同位素和交易性缺勤。一路上完全的「數學」exactly-once很少實現且昂貴。
Q: 在哪裏存儲大型任務附件?
A:在對象存儲(S3/GCS)中,在任務中包含鏈接/ID;減少經紀人和網絡上的壓力。
Q: 如何選擇TTL/可見性?
A:可視性≥ p99處理時間×庫存2-3 ×。TTL任務-減少業務截止日期。
18)結果
強大的隊列系統是傳遞語義,優先級和限制因素之間的平衡。設計密鑰和路由,確保等效性,backoff和DLQ的轉發,在SLA類中分配資源,並註意指標。然後您的背景流程將是可預測的,可持續的和可擴展的-在峰值下沒有驚喜。