來文程序的保障
1)什麼是「秩序」,為什麼需要它
消息順序是單個實體(訂單,用戶,錢包)或整個線程的事件「必須先處理」的關系。它對於不變量很重要:「B之前的狀態A」,「註銷前的平衡」,「n+1之前的版本n」。
在分布式系統中,很少需要全球總道路順序;通常,「按鍵」的本地順序就足夠了。
2)秩序保證的類型
1.Per-partition (log部分中的本地順序)-Kafka:黨內順序保持不變,黨內順序保持不變。
2.按鍵(ordering key/message group)-將所有單鍵消息路由到一個處理的「線程」(Kafka key, SQS FIFO MessageGroupId, Pub/Sub ordering key)。
3.全球總命令-整個系統看到一個順序(分布式日誌/音序器)。價格昂貴,降低了可用性和通量。
4.Causal order(因果關系)是「如果B觀察到A效應,則事件B之後」。無需全局測序器即可通過元數據(版本,Lamport時間/矢量時鐘)實現。
5.Best-effort order-經紀人試圖保持秩序,但是如果失敗,可能會進行重新排列(通常在NATS Core,RabbitMQ中使用多個對齊器)。
3)訂單中斷的地方
一隊列並行消費者(RabbitMQ:每個隊列多個消費者→ interleaving)。
Retrai/重新交付(at least-once),taymauts 「ack」,重新排隊。
Rebalans/feilover(Kafka:政黨/領導人的舉動)。
DLQ/重新處理-「有毒」消息離開DLQ,接下來是邏輯中斷→。
多區域和復制-不同的延遲→同步。
4)「按鍵順序」設計"
密鑰形成「排序單元」。建議:- 使用自然鍵:'order_id'、'wallet_id'、'aggregate_id'。
- 留意「熱鍵」-單鍵可以「鎖定」流(頭對頭鎖定)。如果需要,分解鍵:'order_id#shard (0.. k-1)",在合成器上進行確定性順序重建。
- 在Kafka中-一個鍵→一個部分,訂單將保持在鍵內。
java producer.send(new ProducerRecord<>("orders", orderId, eventBytes));
(鍵='orderId'保證了本地順序。)
5)「訂單與容量」
強大的保修通常與通配符和可訪問性發生沖突:- 每個隊列一個匹配器保留順序,但減少並發。
- At-least-once+並發可以提高性能,但需要等速和/或階還原。
- Global Order為音序器添加了嘻哈→ ↑latentnost和故障風險。
折衷方案:按鍵順序,並行=政黨/團體數,+等效辛基。
6)控制特定經紀人的秩序
Kafka
黨內的秩序。
遵守'max。in.flight.requests.per.connection ≤ 5` с `enable.idempotence=true',這樣制片人的復制品就不會改變順序。
Consumer Group:一方→一方。可以重復交付→將序列/版本保持在業務層中。
事務(read-process-write)保持一致性「讀取/寫入/錯配」,但不創建全局順序。
生產最低限度(生產者。properties):
properties enable.idempotence=true acks=all retries=2147483647 max.in.flight.requests.per.connection=5
RabbitMQ (AMQP)
單個concumer的順序保證為單隊列。多個消息匹配器可能會出現「前進」。
對於順序:完成後,單個consumer或prefetch=1+ack。對於並行-按鍵劃分隊列(sharding exchanges/consistent-hash exchange)。
NATS / JetStream
NATS Core-最好的effort,低潛伏期,訂單可能會中斷.
JetStream:在流/序列中排序;如果重新排列,則可以重新排列concumer,→使用序列和恢復緩沖區。
SQS FIFO
Exactly once processing(有效,通過重復數據消除)和MessageGroupId中的順序。並發是線頭組內的組數。
Google Pub/Sub
Ordering鍵在鍵內給出一個順序;如果出現錯誤,則在恢復之前會阻止發布-請註意後退。
7)保存和恢復秩序的模式
7.1個序列/轉化
每個事件都帶有「seq」/「版本」。Consumer:- 僅在「seq=last_seq+1」時接受事件;
- 否則-在失蹤人員到達之前(「last_seq+1」)放置在等待緩沖區。
pseudo if seq == last+1: apply(); last++
else if seq > last+1: buffer[seq] = ev else: skip // дубль/повтор
7.2緩沖區和窗口(流處理)
Time-window+watermark:在窗口內接受訂單外,在watermark上「關閉」窗口並排序。
Allowed lateness:遲到的頻道(recompute/ignore)。
7.3按鍵按鍵sticky-routing
哈希路由「hash (key)% shards」將所有密鑰事件發送到單個用戶。
在Kubernetes-支持隊列/sherda級別的會話(sticky),而不支持L4 HTTP平衡器。
7.4演員模型/「一鍵流」
對於關鍵單元(錢包):演員依次處理,其余並發由演員數處理。
7.5相等性+重新排序
即使恢復順序,重復也是可能的。將UPSERT按鍵+版本和Inbox結合起來(請參閱「Exactly-once vs At-least-once」)。
8)處理「有毒」信息(poison pills)
保持秩序面臨的挑戰是: 「如果一個消息不處理,如何生活?」
嚴格順序:鎖定密鑰流(SQS FIFO:整個組)。解決方案是按鍵DLQ:我們只將問題密鑰/組轉換為單獨的隊列/手動解析。
靈活順序:允許通行/賠償;我們計算並繼續(不適用於金融/關鍵總量)。
Retrais Policy:有限的「max-deliver」+backoff+avidemential效果。
9)多區域和全球系統
Cluster-linking/復制 (Kafka)不能保證跨區域的全球秩序。優先考慮局部按鍵順序和等效彎曲。
對於truly-global訂單,請使用音序器(中央日誌),但這會影響可用性(CAP:網絡中斷時減去A)。
另一種選擇:某些域(計數器、集合)的causal order+CRDT-不需要嚴格的順序。
10)順序可觀察性
Метрики: `out_of_order_total`, `reordered_in_window_total`, `late_events_total`, `buffer_size_current`, `blocked_keys_total`, `fifo_group_backlog`.
11)反模式
一個隊列+許多不按鍵行駛的求和器-順序立即斷裂。
在沒有偶然性的情況下,通過筆式公共場所的回溯是雙打+順序。
「以防萬一」的全球秩序是潛伏期和成本的爆炸,沒有真正的好處。
SQS FIFO每個組都是完整的頭線。使用MessageGroupId按鍵。
忽略「熱鍵」-一個「錢包」會抑制一切;盡可能將密鑰分成副密鑰。
在單個隊列/組中混合關鍵流和布爾克流-相互影響和順序損失。
12)實施支票
- 確定了保修級別:per-key/per-partition/causal/global?
- 設計了針對「熱鍵」的排序密鑰和策略。
- 已配置路由器:partization/MessageGroupId/ordering密鑰。
- Concumers是通過密鑰隔離的(粘性路由,shard-workers).
- 包括指針上的冪和/或Inbox/UPSERT。
- 已實現sequence/version和reordering緩沖區(如果需要)。
- DLQ by key和retrai with backoff.
- 順序和異序度量:從順序開始,blocked_keys,late_events。
- 遊戲日:重建,節點丟失,「有毒」消息,網絡延遲。
- 文檔:順序不變性,窗口邊界,對SLA的影響。
13)配置示例
13.1 Kafka消費者(最小化秩序障礙)
properties max.poll.records=500 enable.auto.commit=false # коммит после успешной обработки батча isolation.level=read_committed
13.2 RabbitMQ(以並發為代價)
每個隊列一個concumer +'basic。qos(prefetch=1)`
對於並發-多隊列和hash-exchange:bash rabbitmq-plugins enable rabbitmq_consistent_hash_exchange публикуем с хедером/ключом для консистентного хеша
13.3 SQS FIFO
設置MessageGroupId=key。並發=組數。
MessageDeduplicationId用於雙倍保護(在提供程序窗口中)。
13.4 NATS JetStream(ordered consumer,草圖)
bash nats consumer add ORDERS ORD-KEY-42 --filter "orders.42.>" --deliver pull \
--ack explicit --max-deliver 6
14) FAQ
問:我需要全球秩序嗎?
答:幾乎從來沒有。幾乎總是有足夠的按鍵。全球秩序-價格昂貴,並且影響了可用性。
問:如何以嚴格的順序使用「有毒」信息?
答:僅將其密鑰/組轉換為DLQ,其余部分繼續。
Q:可以同時獲得順序和比例嗎?
答:是的,按鍵順序+很多鍵/分期付款+在需要的地方進行偶數操作和重新排序緩沖。
問:更重要的是什麼:順序還是唯一的?
答:對於大多數域,按鍵+有效的異常效果(等效性/UPSERT)排序。運輸可以是空運。
15)結果
訂單是圍繞業務密鑰而不是昂貴的全球紀律的本地保證。設計密鑰和批次,限制「熱」密鑰,使用等效性,並在需要時使用sequence+reordering緩沖區。監視「出訂單」和「鎖定密鑰」指標,測試故障-並且在性能和可用性方面無需犧牲即可獲得可預測的處理。