后端架構師做了一個看起來漂亮的決定:支付平臺三個消費方——通知、欺詐、分析——全部掛到同一條事件總線上,三條規則,三臺函數,干凈利落。六模塊一張圖,架構評審全票通過。誰都覺得這是教科書式的無服務器解耦。
六個月后,數據倉庫要停機遷移,窗口四個小時。這就是標準運維:分析團隊在周日晚上手動禁掉了自己那條規則,免得函數往宕機的倉庫上砸出成噸報錯。周一早上六點重新啟用,按說一切照舊。結果不是。
![]()
那四個小時里,支付系統照常跑完了14000筆交易。通知規則照發推送郵件,反欺詐規則照常評分,偏偏分析規則——因為被禁用了——每一條事件都被事件總線評估后直接丟棄。沒有投遞嘗試,沒有失敗記錄,沒有重試機制,更沒有任何存儲。等周一上午對賬,分析師發現差不多140萬美元的交易就這么從報表里蒸發了。上午九點,審計團隊把問題甩上了桌面。
架構師為三個消費者統一定制事件通道,就是這整件事的起點。那個決定把三種不同可靠性和重放需求的消費模式,塞進了同一種“發完即焚”的投遞模型里。通知和反欺詐是實時、即拋的,丟了也就丟了;但對分析鏈路來說,事件必須可重放、可回溯,哪怕下游休假四小時也不能消失。而這條總線默認的行為恰恰是:如果發布時沒有匹配到啟用的規則,該事件永久消失,不存檔、不排隊、不替你留。
所以真正的問題不是“為什么禁了規則”,而是“為什么用了一個禁規則就等于丟事件的基礎設施”。現在來復盤那四個備選方案,看看哪一個能從根本上避免這個坑。
選項A:給分析規則配死信隊列。這是很多人第一反應——既然投遞失敗就走死信,重試不就行了?但壞就壞在,事件根本沒有投遞失敗。規則被禁用后,總線在每次事件發布時評估:目標規則未啟用,所以不投遞。沒投遞就沒有失敗,不失敗就進不了死信。死信只能兜住函數執行崩潰或超時之類的錯誤,兜不住規則本身被關閉。那14000筆支付連碰都沒碰到死信的入口。
選項B:把分析規則的目標改成消息隊列,再讓函數消費隊列。這套路數等于在事件總線和處理函數之間塞了一個緩沖區。禁規則?根本不用禁。
只要下游倉庫宕機,消費者停止拉取隊列消息,消息就攢在隊列里,等恢復后再繼續消費。總線側規則一直是啟用狀態,事件會照常被投遞到隊列中并持久化,不存在“評估時丟棄”那一出。延遲恢復之后,隊列積壓全部被函數拉走,數據完整。
選項C:把分析團隊接到數據流,而不是事件總線。相比一次性投遞,數據流是一個帶保留期的順序日志,記錄默認最多保留24小時,最長可配到7天。每個消費者維護自己的讀取位置,維護窗口期間消費者直接停掉,恢復后從檢查點繼續讀,就能把全天的事件照單全收。等于數據源天然多出了回放能力,哪怕整個處理管線集體熄火幾個小時也不丟東西。
選項D:啟用事件總線的存檔與回放功能。存檔會把所有發布到總線的事件都存下來,需要的時候可以手動重放,并限定到指定規則。理論上,周一上午一鍵回放,那14000條事件就能重新推到分析規則。
但這里有兩個實際賬單要考慮:一是所有事件都存檔會明顯推高成本;二是回放不是自動的,要等人發現數據丟了再去手動操作。如果審計團隊沒能在周一午餐前揪出問題,延遲會一層層放大,而且回放時還要特別注意防重復。
這四種方案背后是兩類設計哲學:事件驅動里,到底該讓運輸層背可靠性,還是讓存儲層背?事件總線的默認模式把可靠性壓在發布時的匹配上,匹配失效連尸體都不留。消息隊列靠顯式刪除、消費者確認來保證不丟,數據流干脆把“讀沒讀”與“存沒存”徹底解耦,事件總會在那里等你。至于存檔回放,更像是事后的保險絲,不能替代生產管道本應具備的抗宕能力。
復盤這起事故,架構師的錯誤不在于用了事件總線,而在于忽略了不同消費者對事件可靠性的要求根本不一樣。通知和反欺詐是盡力投遞型,實時、無狀態、丟幾個也能忍;分析是審計級,允許延遲但不允許缺失。
把后者的可靠性需求暴力降級到前者的模式里,四個小時的計劃維護就能打出140萬美元的財務窟窿。如果當時給分析團隊單獨掛一條消息隊列,或者直接切到數據流,就只是停機、恢復、消費積壓,三條線自動走完,根本不會出現在審計報告里。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.