任务队列和平衡
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类中分配资源,并注意指标。然后您的背景流程将是可预测的,可持续的和可扩展的-在峰值下没有惊喜。