iT邦幫忙

2026 iThome 鐵人賽

DAY 3
0
Software Development

Re:從零開始做直播代購電商平台系列 第 3 篇

Day 3|留言即下單(上):一條迴圈,吃下三個平台

  • 分享至 

  • xImage
  •  

全景和起手式鋪完,進主線第一戰:一則留言,怎麼變成一筆單。這章講「接進來」和「解析」兩段,佔庫存的攻防留給下一章。主角是把聊天室變成收銀機的那台有限狀態機——我們會直接讀它的程式碼。

當年的管線:一條迴圈,吃下三個平台

留言來自三個源:FB、IG、自建直播間,流量比例大約 100:10:1——FB 是絕對主戰場。三源的取得方式各不相同(Day 1 提過的 webhook、輪詢、自家直推),但殊途同歸:全部落進同一張 message 表,由同一條批次處理鏈消化。所以這章的抓取故事以 FB 的輪詢為主——流量在這裡,坑也都在這裡。當年的接法樸素到有點可愛:

當年的留言管線:三源各自取回、清洗落地,同一條批次鏈解析下單。兩處虛線是這章要算的帳。

三個細節值得放大:

  • FB 的輪詢,是一個自調的速率控制器。 一輪抓完看耗時:超過 2 秒代表留言正湧入,帶著 paging key 立刻接著抓;低於 2 秒代表場子冷,把間隔放慢一點。不用外部設定、不用監控儀表,迴圈自己感知流量、自己調速——小團隊憑直覺做出來的東西,事後看是很標準的 adaptive polling。要付的代價在下游:三源殊途同歸之後擠在同一條批次處理鏈上,FB 爆量時,IG 和自建的單也跟著在佇列裡排隊——FB 的洪峰,是所有人的延遲。
  • 去重鍵選對了:source + message id。 輪詢一定會抓到重疊區間,靠平台原生的留言 ID 當唯一鍵,重抓幾次都不會重複入庫——這是接入層的冪等,做對了後面全順。
  • 落地即保險。 留言清洗成統一格式落地,raw 原文也跟著留了下來;而且 FB 的直播留言就是貼文底下的留言,事後還能整場重抓(順序會和直播中抓到的稍有出入)。事實層的保險,當年買得比記憶中齊全——這份保險最後有沒有派上用場,是這章結尾要算的帳。

順序與重複:LWW 的三個先決問題

「同一人重複留言,以最後一筆為準」——LWW 一句話就講完,但「最後」這個字要先回答三個問題:

  1. 以什麼時鐘為準? 跨 FB/IG/自建三個源,各平台的時間戳基準不可比。當年的答案很務實:以我們落地的順序為主、平台時間戳為輔——三源都寫進同一張 message 表,再由同一條批次處理鏈依序消化,入庫順序就是全域順序。單一消費者的意外好處:你自己就是時鐘。這條時間線的權威性有個旁證:同一場直播的留言,事後當成貼文留言整場重抓,順序會和直播中抓到的不完全一樣——平台自己都不給你一個穩定的順序,你落地的那份,就是唯一的那份。
  2. 覆蓋的粒度是什麼? 是 key+style 級:後留的 2601藍+1 只蓋藍色,先前的 2601紅+2 還在。而且這是一個帶副作用的 LWW——蓋掉 +2 變 +1,購物車數量要改、賣出數量表要補回差額。一般系統的 LWW 丟掉舊值就完事,這裡的舊值佔著庫存,覆蓋即補償。
  3. 「最後」的邊界在哪一場? 主播有重喊的需求——同一個 key 重新開賣,舊場次的單全部清除、完整重算,客人要重新留言。所以 LWW 的有效鍵其實是「人+場次+style」:重喊即斷代。被清單的客人沒有系統通知,純靠主播口播——這也是 feature:主播就是這個平台的通知系統,「現在不留就沒了」的急迫感,正是直播銷售的引擎。

失敗的單去哪了:重來版的答案

當年最痛的取捨在管線末端:batch 沒有進度記錄,處理失敗的留言直接略過——那位客人的單無聲消失。這是刻意的:停下來救單,主播眼中的庫存就舊了;跳過,數字永遠最新。為了主播的新鮮度,犧牲顧客的完整性。

不過「掉單」不是一個洞,是三個不同的洞——把上面那份保險逐格對上去,帳才算得清:

  • 抓漏(直播中輪詢漏了留言):罩得最好的一格。事後可以整場重抓,Django admin 裡甚至真的做了一顆「從 FB 貼文重抓」的 action——只是它從來沒被按過。回頭看不全是怠惰:這門生意處理漏單的方式本來就是現場的,主播再喊一次、客人再留一次,急迫感把傷害當場吸收掉;事後由系統悄悄補單,反而不是直播的節奏。
  • 清洗漏接(規則沒認出的留言):raw 在,理論上能拿歷史留言回測、重放修正——但這條回測管線沒建,漏接了什麼仍然只能猜。
  • 處理失敗(FSM 或扣庫存那一步炸了):真正的黑洞。訊息早已入庫,接入層做對的冪等去重,這時反而把重抓擋在門外;批次略過就不回頭。三個洞裡,唯一連理論上的救援都沒有的一個。

盤點的結論有點諷刺:能自癒的那格(主播重喊就解決)保險買好買滿,真正的黑洞一分保險都沒有。重來版不推翻「新鮮度優先」的優先序,要補的是讓救援自己會跑——那顆沒人按過的按鈕已經證明,靠人記得去按的補救,等於沒有補救:

  • 每源一條處理鏈(取回端本來就各自獨立),FB 的洪峰不再拖著 IG 和自建的單一起排隊;
  • 把留著的 raw 升格為正式的事件流資產——不只是存著,而是接上重放與回測的入口:清洗規則漏接,重放就能補;
  • 快路徑照樣跳過失敗——但失敗的事件進 dead-letter 佇列,一條慢速補救路徑自動事後重放(投遞語意那套在這裡全用得上),「處理失敗」從黑洞變成「晚點到」;
  • 解析與扣庫存拆成兩段:解析是純函式、可以平行,扣庫存才需要排隊——當年它們擠在同一個 batch 迴圈裡,慢的拖著快的。

一句話:快用「晚點處理」換,不用「放棄客人」換。 主播照樣看到最新的數字,而那位失敗的客人,幾秒後會被補救路徑撈回來。

反思

誠實面對那條虛線

但我不想把當年美化成處處是智慧。圖上那條紅色虛線——失敗直接略過——是真實傷過客人的:失敗的單無聲消失,而我們連道歉都不知道要跟誰道。三個洞盤點下來,最刺的一課是保險的錯位:我們把保險買在商業節奏本來就會自己吸收的那格(重抓按鈕沒人按,因為主播重喊就解決了),而真正不會自癒的黑洞,一分保險都沒有——連受傷的人數都沒量過(沒收到抱怨,往往只是客人不知道自己該抱怨)。保險的價值不在買了多少,在有沒有蓋住不會自癒的傷口;而要知道哪裡不會自癒,得先承認系統會受傷,並且去量它。如果這系列只能帶走一句話,我希望是這句:留下事實是上半場,決定「誰、什麼時候動用它」才是下半場——躺在倉庫裡的 raw、沒人按的按鈕,對掉單的客人來說,和不存在是同一回事。

這條管線的心臟——把留言變成訂單的那台手寫狀態機——值得專門一天。明天,我們直接讀它的程式碼。


本文改寫自我的部落格系列《Re:從零開始做直播代購電商平台》,本篇完整版:https://blog.aidan.tw/blog/rezero-comment-order/


上一篇
Day 2|起手式:五個元件與一條 CI/CD
下一篇
Day 4|留言即下單(下):為拇指設計的迷你語言
系列文
Re:從零開始做直播代購電商平台 共 11 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言