Azure認證帳號開戶 Azure 支付網關超時解決方案
Azure 支付網關超時?別急著重啟,先搞懂它在生什麼氣
你是不是也遇過這種場景:客戶點下「立即付款」,畫面卡住三秒、五秒、十秒……最後跳出一張冷冰冰的錯誤訊息:「請求逾時,請稍後再試」。你盯著 Log Analytics 裡那行 504 Gateway Timeout,心裡默默唸了三遍佛號——不是阿彌陀佛,而是「Azure…拜託你給我個活路」。
這不是玄學,是工程現實。Azure 支付網關(無論是整合 Stripe、Adyen、PayPal 或自建 Payment Orchestrator)一旦出現超時,背後往往不是單一元件失靈,而是一連串「時間接力賽」的崩潰:API Gateway 等不及函數回應、Functions 自己被 runtime 殺掉、下游支付廠商網路抖動、甚至只是某台 VM 上的 DNS 解析慢了 800 毫秒……而這 800 毫秒,剛好就是你 SLA 裡寫死的 2 秒上限。
超時不是 bug,是系統在發 SOS 信號
Azure認證帳號開戶 先破除一個迷思:「超時」從來不是故障,而是防禦性設計。Azure 的每層元件都預設了安全閥值:
• Application Gateway / Front Door:預設空閒逾時 4 分鐘,但健康探針逾時僅 30 秒;
• Azure Functions(Consumption Plan):HTTP 觸發函數最長只能活 10 分鐘(Linux)或 30 分鐘(Windows),且預設「無活動即休眠」;
• Logic Apps:HTTP 動作預設逾時 2 分鐘,不可超過 6 分鐘;
• 下游支付 API(如 Stripe):雖承諾 99.9% < 500ms,但「跨太平洋路由抖動 + 銀行端清算驗證」可能飆到 8~12 秒。
當這些閾值像多米諾骨牌般倒下,你看到的就只有一個結果:客戶付款失敗、營收流失、客服電話被打爆——而你還在 Kibana 裡翻三天前的 traceId。
四步診斷法:先抓兇手,再開藥方
第一步:鎖定「誰先喊停」
別直接改 timeout 數值!先用 Application Insights 的「End-to-End Transaction」視圖,看哪個 span 標紅了。常見模式有三種:
• 若 requests 表顯示 duration > 230s 但無依附 dependency,代表是函數內部邏輯卡關(例如同步呼叫 DB + 無索引掃描);
• 若 dependency 跟 request duration 幾乎相等,且 target 是 https://api.stripe.com,那就是第三方延遲;
• 若 request duration 約 240s 整,且 dependency 沒記錄——八成是 App Gateway 的 4 分鐘空閒逾時在背後補刀。
第二步:驗證函數 Runtime 是否被腰斬
在 Function 的 host.json 加入:{"logging": {"logLevel": {"default": "Information"}}, "extensionBundle": {"id": "Microsoft.Azure.Functions.ExtensionBundle", "version": "[4.*, 5.0.0)"}}
並在 C# 函數開頭加一行:log.LogInformation($"Function start at {DateTime.UtcNow:O}");
結尾加:log.LogInformation($"Function end at {DateTime.UtcNow:O}");
若 log 裡只有 start 沒有 end,且時間差接近 10 分鐘——恭喜,你的函數被 Consumption Plan 的 runtime 強制終止了。
真正能落地的五大解決方案
① 別硬扛,把「同步支付」切成「異步訂單」
這是 ROI 最高的改造。客戶點付款 → 你立刻回傳 202 Accepted + location: /payments/{id}/status → 後台用 Durable Functions 啟動三階段 workflow:
1. PreCheck:驗證卡片格式、餘額預扣(Call Stripe /authorize);
2. Confirm:收到銀行清算結果後更新訂單狀態;
3. Notify:透過 SignalR 推送前端,或寄 Email/SMS。
這樣一來,前端等待從「10 秒焦慮」變成「200ms 安心」,而後台有 30 分鐘慢慢跟銀行拉鋸。
② 重試不是亂 retry,是「智慧退避」
對 Stripe API,別用固定 3 秒重試。改用 ExponentialBackoff + jitter:var policy = Policy.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: (retryAttempt, outcome, context) =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) +
TimeSpan.FromMilliseconds(new Random().Next(100, 500)));
重點在 jitter —— 避免全站同時重試造成下游雪崩。
③ 調整函數的「生存權」設定
若必須同步響應,Upgrade 到 Premium Plan(彈性擴充),並在 host.json 設定:{"functionTimeout": "00:10:00"}
同時關閉「Always On」以外的休眠機制。注意:Consumption Plan 無法突破 10 分鐘硬限制,別浪費時間 hack。
④ 在 API Gateway 層加「健康快篩」
用 Application Gateway 的「自訂健康探針」,每 15 秒檢查支付函數是否真的活著:
• 探針 URL:/api/health?probe=payment
• 成功條件:HTTP 200 + Response body 包含 "status":"ready"
• 失敗時自動將該 instance 從 backend pool 移除,避免把流量導向已卡死的節點。
⑤ 監控不能只看「成功率」,要看「P95 延遲分佈」
在 Log Analytics 建立警示規則:requests
| where timestamp > ago(5m)
| where name has "PaymentProcess"
| summarize p95_duration = percentile(duration, 95) by bin(timestamp, 1m)
| where p95_duration > 3000
當 P95 超過 3 秒就觸發 Slack 告警——比等客戶投訴早 8 分鐘發現問題。
那些踩過的坑,幫你省下三天工時
• 別信「本地測試 OK」:Local Functions 走 localhost,沒經過 App Gateway、WAF、NAT Gateway,網路路徑完全不同;
• DNS 必須強制指定:在函數內用 HttpClient 呼叫 Stripe 時,加上:client.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");
// 並在 Startup.cs 設定 DNS 解析緩存
ServicePointManager.FindServicePoint(new Uri("https://api.stripe.com")).ConnectionLeaseTimeout = 60000;
• Log 裡的「timestamp」不等於「實際發生時間」:Application Insights 的 timestamp 是資料上傳時間,要用 operation_SyntheticSource + client_IP 交叉比對才準。
最後說句真心話:支付系統的穩定性,從來不取決於你寫了多少行 code,而在於你願意為「最壞情境」預留多少緩衝。超時不是失敗的句點,而是系統在提醒你:「嘿,這條路太窄了,要不要繞一下山?」——而真正的專業,就是知道哪條岔路通往 99.99% 的可用性,又不讓客戶多等半秒。

