架構2026年8月3日閱讀時間 8 分鐘

Webhook vs 輪詢:依失敗模式選擇,不是跟風

輪詢浪費請求,但出問題時能優雅降級。Webhook 有效率,卻會無聲地失敗。另外談多數認真的整合最後都會做的混合架構。

輪詢 · 缺口會自己補上你的迴圈當機 3 小時下一次輪詢since=cursor已補齊復原不需要任何你還沒寫過的程式碼Webhook · 缺口看不見服務商500s 3 小時重試後放棄什麼都沒有沒有錯誤,沒有游標任何地方都沒有訊號沒有資料,看起來和沒有活動一模一樣
該比的不是每小時幾個請求。該比的是每種模式壞掉時,會留下什麼。

常見的說法是,輪詢是陽春的做法,等你進階了就該改用 Webhook。這個故事很工整,卻常常把團隊帶向錯誤的架構,頻率高得出人意料。

有用的問題不是哪一個比較有效率。Webhook 比較有效率,而且差距懸殊。有用的問題是兩者壞掉時各會怎樣,因為凌晨 3 點你要面對的就是這個特性,而兩者幾乎完全相反。

重點摘要

  • 輪詢的失敗大聲又可復原:你會發現,而下一次輪詢就會補上
  • Webhook 的失敗無聲又永久:沒有任何地方報錯,事件就這樣不見了
  • Webhook 逼你自己補上輪詢免費就有的基礎設施:重試、去重、順序,以及簽章驗證
  • 大多數正式環境的整合最後都會變成混合架構:Webhook 負責延遲,對帳輪詢負責正確性
  • 如果只能做一種,而正確性比延遲重要,就用輪詢

常見的比較方式

輪詢Webhook
延遲你的輪詢間隔,平均是間隔的一半接近即時
白費的請求大部分沒有
誰負擔成本你發送端
需要公開端點否是
順序由你控制不保證
建置一個 HTTP 呼叫加一個迴圈端點、TLS、簽章驗證、去重、重試

看這張表,Webhook 在大家通常最在意的那幾列都贏。這也是為什麼那麼多團隊改用 Webhook、上線,然後在接下來兩個月裡悄悄地遺失事件。

真正決定勝負的比較

換個問法:這個壞掉時,我要怎麼知道?

輪詢退化的方式會自己告訴你。伺服器當機,迴圈就不會跑。等伺服器恢復,下一次輪詢會問「從我上一個游標(cursor)之後,有什麼變了」,然後拿回全部。復原是自動的,不需要任何你原本沒寫的程式碼。API 當機時,你的輪詢會收到 500,你在自己的錯誤率裡馬上就看得到。拉取(pull)模式代表你永遠知道自己最後一個確定正常的位置,光是這一個特性就價值非凡。

Webhook 的失敗方式,從你這一側完全看不見。一次出包的部署,讓端點連續六個小時回 500。服務商重試、放棄,然後就不管了。你的 log 看不出任何異常,因為從沒收到的請求,你無從記錄。系統裡沒有游標,沒有缺口偵測,也沒有任何地方顯示「我落後了 400 個事件」。沒有資料,看起來和沒有活動一模一樣;在一個安靜的週日,你根本分不出兩者。

輪詢 · 缺口會自己補上你的迴圈當機 3 小時下一次輪詢since=cursor已補齊復原不需要任何你還沒寫過的程式碼Webhook · 缺口看不見服務商500s 3 小時重試後放棄什麼都沒有沒有錯誤,沒有游標任何地方都沒有訊號沒有資料,看起來和沒有活動一模一樣

這種不對稱就該是決定的依據。輪詢最糟的情況,是資料過時,但你之後會補上。Webhook 最糟的情況,是資料缺漏,而你永遠不會知道。

次一層的成本也由此而來。用了 Webhook,重試邏輯就歸你管,因為服務商的重試期限有限,就算是大方的服務商,三天後也會放棄。冪等性歸你管,因為至少一次(at-least-once)傳遞代表重複是必然的。順序保護歸你管,因為重試會讓事件 #2 超前事件 #1。簽章驗證歸你管,因為你的端點是公開的,任何人都能 POST 過來。你還得擁有一個永遠在線的公開 HTTPS 端點,這和「我們的服務通常都在線」是不同等級的可用性要求。

這些單獨看都不難。加在一起,就是「用 Webhook 就好」變成兩週專案的原因,而且在你以為做完的時候,其實還沒做完。

輪詢真正是正確答案的時候

遇到下面這些情況,別讓效率的說法動搖你。

最明顯的是你不需要的低延遲。每晚的會計同步、庫存對帳、CRM 資料補充工作:如果 5 分鐘的延遲沒人會注意到,你就是在為一個對誰都沒有價值的特性,付出實實在在的複雜度。

再來是沒有地方可以接收。桌面程式、VPN 後面的內部工具、在某人筆電上跑的工作。用隧道把 Webhook 接進 localhost 是開發技巧,不是正式環境的架構;這條界線在哪裡,我們在「為什麼只靠 ngrok 不夠」寫過。

團隊最常判斷錯的,是正確性比延遲重要、而期限只剩一週的時候。一個你能想清楚的游標式輪詢,勝過一條做到一半的 Webhook 管線。

流量小的時候也一樣。每 5 分鐘輪詢一次,一天是 288 個請求,任何速率限制都不會注意到,維運上的簡單也是免費附帶的。服務商自己的 Webhook 不穩定時也是如此:如果它的傳遞不可靠,事件紀錄又很簡陋,輪詢它的 API 比信任它的發送端更可靠。

Webhook 值得那些複雜度的時候

有人在等的時候,Webhook 就值回票價。付款確認、部署狀態、一則聊天訊息。這時連一秒都嫌久,而要讓輪詢快到足以假裝即時,就得不停狂打 API。

事件稀少又無法預測時,Webhook 也會贏。為了一天只發生兩次的事去輪詢,代表大約 99.9% 的請求什麼都拿不到,這正是 Webhook 被設計來處理的情況。在另一個極端,流量可能讓輪詢根本不可行:一萬家商店各自需要 30 秒的新鮮度,一天就是 2,880 萬個請求,遠在那之前你就會撞上速率限制。

最後一種是長時間執行的工作。影片轉檔、模型推論、批次匯出。服務商知道什麼時候完成,你卻只能用猜的,所以 AI 和媒體類的 API 都大力推 Webhook。詳見「給 AI agent 用的 Webhook」。

混合架構:反正你最後都會做的那一種

幾乎每個成熟的整合最後都會走到這裡,而主動走到這一步,比出了事故才走到便宜得多。Webhook 負責延遲,定期的對帳輪詢負責正確性。

// Fast path: webhook, near-real-time
export async function POST(req: Request) {
  const event = await verify(req);
  await upsertEvent(event);           // idempotent by provider event id
  return new Response(null, { status: 200 });
}

// Safety net: every 15 minutes, ask what we should have
async function reconcile() {
  const now = new Date();             // before listing, so nothing updated mid-sweep is skipped
  const since = await getLastReconciledAt();
  for await (const obj of provider.list({ updated_since: since })) {
    await upsertEvent(obj);           // same idempotent path, no-op if webhook won
  }
  await setLastReconciledAt(now);
}

對帳工作的成本是一個排程函式,而且重用你已經寫好的程式碼。換來的是把 Webhook 最糟的特性,也就是無聲又永久的遺失,變成「最多落後 15 分鐘」。這是完全不同的風險。最糟的情況甚至不是漏掉一筆付款,那總會有人發現;而是漏掉一個 access.revoked,那就沒有人會發現。

讓它成立的條件,是兩條路徑都走同一個冪等的寫入邏輯。Webhook 已經處理過,對帳就什麼都不做;還沒處理,對帳就會補上。這只在你用服務商的事件 ID 去重,而不是用自己的收到時間時才成立。做法寫在「同一個 Webhook 你收到了兩次」。

接著替重疊的部分加上監測。計算對帳多常找到 Webhook 漏掉的東西。這個數字就是 Webhook 管線真實的遺失率,也是你唯一能得到的誠實量測。如果一個月都是零,你的 Webhook 很健康。如果突然飆高,就代表發生了一次你原本永遠不會發現的服務中斷。

大家忘了的中間地帶

輪詢不一定要很笨。

游標式輪詢,也就是用 ?since=<cursor>,而不是「全部抓回來再比對差異」,成本低得多,而且永遠不會漏掉任何一筆,因為游標只在處理成功後才前進。

條件式請求,也就是搭配 ETag 的 If-None-Match 或 If-Modified-Since,會回傳沒有主體的 304。很多服務商不會把這些請求算進速率限制。你可以用很低的成本積極輪詢。

長輪詢(long polling)給你接近 Webhook 的延遲,加上拉取模式的復原能力。它比較少見,但有提供的地方,往往能同時拿到兩邊的好處。

如果你是因為固定間隔全部重抓的成本而排除輪詢,你比較的可能是它最差的版本。

AnyHook 的角色

Webhook 之所以無聲地失敗,是因為沒有人記錄本來應該收到什麼。AnyHook 就是那份紀錄。

服務商 POST 到 in.anyhook.net/you/app。每個事件在嘗試傳遞之前就已經持久儲存,所以:

  • 缺口變得看得見。傳遞失敗會被計數、顯示出來,並在連續失敗 1、5、20 次時發出警示,「無聲」這個說法就不再成立
  • 復原靠重播,不靠對帳工作。選取伺服器當機的那段時間,從事件紀錄重新傳遞。重播失敗的事件不計入每月配額,不過有每日上限,也會用到你的運算額度
  • 基礎設施的成本由我們一次付清:指數退避重試、持久的事件紀錄,以及一個統一的 AnyHook-Signature 標頭,不必每個服務商各寫一套驗證

這並沒有讓對帳輪詢失去意義。服務商根本沒送出的事件,任何 Webhook 中繼服務(webhook relay)都變不出來。但它把失敗模式從「無聲且永久」縮小成「有紀錄、有警示、可重播」,而這正是混合架構原本替你換來的大部分好處。

想在註冊之前先看看,一個指令就能拿到可用的接收網址(inbound URL),不需要帳號:

curl -X POST https://anyhook.net/api/v1/quickstart

回應裡有一個 inbound_url,讓你設定給服務商;一個 api_key,用來透過 API 讀取事件紀錄;還有一個 claim_url。這個端點是暫時的:除非你開啟認領網址並登入,否則端點和它的事件會在 7 天後刪除。認領之後,網址維持不變。

結論

依失敗模式來選。輪詢會慢,但能自我修復;Webhook 很快,卻會悄悄弄丟東西。如果延遲既不影響人,也不影響速率限制,輪詢就不是新手選項,而是最糟情況比較好的那一個。如果你真的在正式環境跑 Webhook,就在需要之前加上對帳輪詢,因為它產生的指標,是你唯一能知道自己實際遺失多少事件的方法。

所有文章2026年8月3日 · 8 分鐘

別再漏掉 Webhook。

換一個網址,就有重試、事件紀錄和一鍵重播。或者先親眼看看:註冊後跑一次 30 秒的當機演練,看著一個事件在伺服器當機時保存下來。不需要伺服器。