タスクキューとバランシング
1)タスクキューの理由
ジョブキュー/ワークキューは、メーカーとパフォーマーを時間と速度で切断します:- ピークを滑らかにする:フロントとヘビーサブシステム間のバッファ。
- SLAの安定化:ロードクラスの優先順位と分離。
- フォールトトレランスを簡素化:リトレイ、DLQ、リステージング。
- 水平スケール:APIを変更せずにワーカーを追加します。
典型的なドメイン:決済処理、通知、レポート/メディア生成、ETL/ML後処理、外部APIとの統合。
2)モデルと基本的な概念
Producer:タスクを公開します(ペイロード+メタデータ:idempotencyキー、優先度、期限)。
キュー/トピック:タスクのバッファ/ログ。
Worker:タスク、プロセス、確認(ack)またはエラーで返されます。
Visibility Timeout/Lease:処理期間中の「rent」タスク、after-auto-redelivery。
DLQ (Dead Letter Queue):試行/致命的なエラーの制限後にタスクを「埋める」。
Rate Limit/Concurrency:ワーカーごと/キューごと/テナントごとの消費制限。
- プル:作業者自身がタスクを要求します(負荷を投与します)。
- プッシュ:ブローカーフラフ;「満ちる」弱い労働者に対する保護を必要とします。
3)配信と確認の意味論
At-once:リトレイなし;より速く、しかし可能な損失。
少なくとも1回(ほとんどのキューではデフォルト):重複が可能です→ハンドラのidEmpotencyが必要です。
正確に一度:アプリケーションレベル(idempotency、 dedup、 transactions/outbox)で達成。ブローカーは助けることができますが、「魔法の弾丸」ではありません。
- 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:重複したキータスクを組み合わせます。
並列キャップ:タスクタイプ/インテグレーション(外部API)による並列性の厳密な制限。
4.3 GeoとShardening
キーによる破片(テナント/id)→データの局所性、破片内の安定した順序。
Sticky caches/resources: 「attached」状態のワーカーへのハッシュルーティング。
5) Retrai、バックオフおよびDLQ
指数関数backoff+jitter: 'base 2^attempt ± random'。
タスクごとに最大試行回数と合計期限(タイムツーダイ)。
エラーの分類:'retryable'(ネットワーク/制限)、'non-retryable'(検証/ビジネス禁止)。
駐車/遅延キュー:遅延タスク(たとえば、15分後に繰り返します)。
DLQポリシー:「有毒な」メッセージがどこでどのような条件で得られるかを示すようにしてください。reprocessorを提供して下さい。
6) Idempotencyおよび重複除外
タスクのIdempotency-Key;最後のNキーのTTLを持つstore (Redis/DB):- 見る→skip/merge/result-cache。
- 自然キー:ランダムなUUIDの代わりに'order_id/ payment_id'を使用します。
- Outbox-ビジネストランザクションを使用して、タスクの事実とそのステータスを1つのデータベーストランザクションに記録します。
- 青色で正確に1回:'UPSERT'キー、バージョン管理、キュー+データベースのidempotencyでの「at-lost-once」。
7)マルチテナンシーとSLAクラス
キュー/ストリームをクラス別に分ける:'critical'、 'standard'、 'bulk'。
テナントあたりのクォータと優先順位(金/銀/ブロンズ)。
分離:P0の下の労働者のプールを捧げて下さい;background-別のクラスタ/ノード。
入場管理:締め切りで処理できる以上のものを受け入れないでください。
8)労働者をAutoscaling
スケーリングのメトリクス:キューの深さ、到着率、処理時間、SLA期限。
KEDA/Horizontal Pod Autoscaler: SQS/Rabbit/Kafka lag depthトリガー。
制限要因:外部レート制限API、データベース(バックエンドを破壊しないでください)。
9)技術の選択およびパターン
9.1 RabbitMQ/AMQP
取引所:直接/トピック/fanout;キュー-ack/ttl/DLQ(デッドレター交換)。
プリフェッチ(QoS)は「、作業者のタスク数」を規制します。
ini x-dead-letter-exchange=dlx x-dead-letter-routing-key=jobs.failed x-message-ttl=60000
9.2 SQS(およびアナログ)
可視性タイムアウト、DelaySeconds、 RedrivePolicy (DLQ)。
Idemotence-アプリケーション上で(dedupテーブル)。
限界:バッチ1-10ポスト;愚かなあざに焦点を当てる。
9.3 カフカ/NATSジェットストリーム
大規模パイプラインの場合:高スループット、保持/再生。
ログオーバーのタスクキュー:1つのタスク=1つのメッセージ;/主体の仕切りを通したキー制御ごとの1人の労働者。
Retrai:バックオフを持つ個々のトピック/サブジェクト接尾辞。
9.4 Redisキュー(Sidekiq/Resque/Bull/Celery-Redis)
非常に低いレイテンシー;単一飛行のための安定性(RDB/AOF)、再試行キーおよびロックキーのために見て下さい。
「軽い」仕事のために適した、長期retensionのためではない。
9.5つのフレームワーク
Celery (Python)、 Sidekiq (Ruby)、 RQ/BullMQ (Node)、 Huey/Resque-既製のリトレイ、スケジュール、ミドルウェア、メトリック。
10)ルーティングとバランシングスキーム
ラウンドロビン:均等ですが、タスクの「重症度」を考慮していません。
加重されたRR:労働者容量/プールによる配分。
Fair/Backpressure-aware:作業者は準備ができているときだけ新しいタスクを拾います。
優先レーン:クラスごとにキューを分けます。ワーカーは[P0→……→Pn]の順番で読みます。
ハッシュルーティング:'hash (key)% shards'-ステートフル/キャッシュ処理用。
11)タイムアウト、締め切り、SLA
タスクごとのタイムアウト:(ワーカーコード内の)作業の内部「リース」≤ブローカーの「可視性タイムアウト」。
グローバル期限:タスクはT時間後に意味がありません-NACK→DLQ。
予算に応じて:締め切りが近づいたときに作業を削減(ブラウンアウト)(部分的な結果)。
12)観測・管理
12.1メトリクス
'queue_depth'、 'arrival_rate'、 'service_rate'、 'lag' (Kafka)、 'invisible_messages' (SQS)。
'success/failed/reited_total'、 'retry_attempts'、 'dlq_in_total'、 'processing_time_ms {p50、 p95、 p99}'。
'idempotency_hit_rate'、 'dedup_drops_total'、 'poison_total'。
12.2ログ/トレース
相関:'job_id'、 'correlation_id'、重複除外キー。
'retry/backoff/dlq'をイベントとしてマークします。スパンの最初の要求からのリンク。
12.3ダッシュボード/アラート
トリガー:depth> X、 p99> SLO、 DLQの成長、スタックタスク(visibility expired> N)、ホットキー。
13)安全性とコンプライアンス
テナント分離:個々のキュー/キースペース、ACL、クォータ。
トランスポートおよび/または「安静時」での暗号化。
ペイロードのPII最小化;粗いPIIの代わりにhash/ID。
秘密:タスク本文にトークンを入れないでください。vault/refsを使用してください。
14)アンチパターン
独自性のないレトライ→重複操作/お金「二度」。
"すべてのための"1つの巨大なキュー→"孤立せず、予測不可能な遅延。
DLQのない無限のレトライ→永遠の「毒」タスク。
可視性タイムアウト<処理時間→カスケード複製。
キュー→ネットワーク/メモリ圧力の大きなペイロード。オブジェクトstorに保存してリンクを転送することをお勧めします。
backpressure→workersのchokeのない押しモデル。
重要なタスクとバルクなタスクを1つの作業者のプールに混在させます。
15)実装チェックリスト
- タスクをSLA (P0/P1/P2)とボリュームで分類します。
- 目的のセマンティクスと保持を持つブローカー/フレームワークを選択します。
- キー、優先順位、ルーティング(ハッシュ/シャード/優先レーン)を設計します。
- バックオフ+ジッターレトレイとDLQポリシーを有効にします。
- idempotency (keys、 upsert、 deadstore with TTL)を実装する。
- タスクごと、可視性、および一般的な期限のタイムアウトを設定します。
- 統合/テナントによる同時性とレートの制限。
- ヒューズによる深度/遅延自動スケーリング。
- メトリック/トレース/アラート;「嵐」とDLQオーバーフローのランブック。
- 失敗のテスト:労働者の落下、「毒」メッセージ、過負荷、長いタスク。
16)サンプル構成とコード
16.1セロリ(レディス/ウサギ)-ベースフロー
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カフカ-レベル別のレトレイ
orders -> orders.retry.5s -> orders.retry.1m -> orders.dlq
(スケジューラ/cron-consumer経由の遅延配信で転送します。)
16.4 NATS JetStream-消費者のバックアップ
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:いつプッシュ対プルを選ぶか?
A:引きは自然な背圧および「正直な」バランスをとることを与えます;プッシュは低速で簡単で、最小限のTTFBが必要な場合は簡単ですが、リミッターが必要です。
Q:熱いキーを避ける方法か?
A:コンポジットキー('order_id% N')、バッファおよびバッチプロセスによるシャードは、キーごとの制限を入力します。
Q:「一度だけ」することは可能ですか?
A:実質的に-idempotenceおよびトランザクションアウトボックスを通して。完全に「数学的」に正確に一度は達成可能であり、すべての方法で高価であることはめったにありません。
Q:大きなタスクの添付ファイルを保存する場所はどこですか?
A:オブジェクトストレージ(S3/GCS)、およびタスク-link/ID;ブローカーとネットワークの圧力を軽減します。
Q: TTL/可視性を選ぶ方法か?
A:在庫2-3 ≥ × p99処理時間×可視性。TTLタスク-ビジネス期限の短縮。
18)合計
強力なキューイングシステムは、配信セマンティクス、優先順位、制約のバランスです。キーとルーティングを設計し、idempotencyを確保し、バックオフとDLQでリトレイし、リソースをSLAクラスに割り当て、メトリックを監視します。その後、バックグラウンドプロセスは予測可能で安定したスケーラブルになります。