題幹與適用場景
一個 API 請求依序呼叫使用者、目錄與推薦三個 gRPC 服務。使用者可能離開頁面,推薦服務也可能短暫超時。系統要控制端到端 延遲、釋放服務端資源、避免重試放大,並在寫入取消後維持可查詢狀態。
gRPC 文件把 deadline 定義為客戶端願意等待的上限,超時會產生 DEADLINE_EXCEEDED;取消會立即終止不再需要的 RPC。高分 回答要分開預算傳播、重試邊界、串流清理和未知結果,不能只為每層設定固定五秒。
面試官考察點
看候選人是否使用絕對 deadline 或剩餘預算、把取消傳給子 RPC 與資料庫、讓重試共用總預算,以及不把「沒有回應」當成寫入未執行。 還要說明觀測欄位與壓測驗證。
回答前需要澄清的問題
- 頂層 SLO、客戶端 deadline 和部分結果規則是什麼?
- 三個服務是唯讀還是包含扣款等副作用?
- 哪些狀態可重試,是否有冪等鍵?
- 串流訊息能否重複,是否支援游標或重連?
- 資料庫和外部 API 是否接受取消?
- 取消後要查詢狀態、補償還是對帳?
30 秒回答框架
「入口建立絕對 deadline,子呼叫只繼承剩餘預算,不重新得到完整超時。父取消立即傳給 RPC、資料庫和可取消工作;重試共用總 deadline、次數上限與退避。唯讀可在安全錯誤上重試,寫入用冪等鍵或狀態查詢。串流結束釋放游標與連線,記錄每層剩餘預算、 取消原因和最終狀態。」
分步驟深入解答
第一步:定義端到端時間預算
入口把客戶端 deadline 視為不可突破的上限。服務收到後計算 remaining = deadline - now,為子呼叫設定不晚於它的時間, 並保留排隊、序列化和回應餘量。
parent_deadline = 2.0s from request start
child_deadline = min(parent_deadline, now + remaining_budget)不要每層再等兩秒,否則三層最壞超過六秒。應用仍用單調時間監控排隊與工作階段。
第二步:傳播取消並真正停止工作
離開頁面、顯式取消和 deadline 到期都要觸發取消。服務端 handler 監聽 context 的 cancellation token,傳給資料庫、HTTP、佇列和串流迭代器。 只返回錯誤但繼續背景工作會造成洩漏。
取消時釋放連線、暫存檔與鎖,停止新訊息;已提交交易不能靠取消撤銷,後續用查詢或補償處理。
第三步:在剩餘預算內重試
只重試明確瞬時錯誤,如 UNAVAILABLE,限制次數、指數退避和隨機抖動。每次沿用同一 deadline,不能重設計時器;到期立即停止。
for attempt in 1..maxAttempts:
if remaining(deadline) <= backoff: stop
result = call(child, deadline, cancellation)
if result is success or permanent_error: return result
sleep(jittered_backoff, cancellation)
return DEADLINE_EXCEEDED統一重試責任,監控各層 attempt 與 retry amplification。
第四步:區分讀取與寫入
唯讀可依契約重試;寫入要有操作 ID、唯一約束和狀態查詢。取消發生在外部寫入後,客戶端得到的是 UNKNOWN,不能換新 ID 盲重試。
PENDING -> CONFIRMED
\-> FAILED
\-> UNKNOWN (reconcile before retry)狀態查詢返回權威結果;deadline 只控制等待,不能提供跨系統原子回滾。
第五步:處理串流 RPC 生命週期
串流設定總 deadline,必要時設定閒置超時;每次讀取檢查取消。導航離開後取消,服務端停止產生訊息並關閉游標或訂閱。重連需使用游標或序號, 不能從頭重播造成副作用。
記錄最後訊息時間、取消原因、重連次數與積壓。優雅停止時讓活躍串流在剩餘 deadline 內完成或返回可識別狀態。
第六步:驗證傳播和資源邊界
測試父取消、各子服務超時、資料庫慢查詢、UNAVAILABLE、永久錯誤、串流中斷、寫入後回應遺失、重試和優雅停止。斷言子呼叫不超過父 deadline, 取消後無背景洩漏,重試不超預算。
記錄 RPC 方法、trace ID、剩餘時間、attempt、狀態碼、取消來源和操作 ID,不記敏感載荷。壓測觀察連線、佇列、重試放大與尾延遲。
高品質示範回答
「入口建立絕對 deadline,聚合層把剩餘預算傳給三個子服務。handler 將取消傳到資料庫、HTTP 和串流迭代器,返回前釋放資源。唯讀只在瞬時錯誤上有限重試, 共用總 deadline。」
「寫入用操作 ID,取消後標記 UNKNOWN 並查狀態;串流用總 deadline、游標和重連。注入父取消、子超時、回應遺失和優雅停止,驗證沒有洩漏與重試放大。」
常見錯誤
- 每層重設完整超時 → 突破入口 SLO → 傳播絕對 deadline。
- handler 返回就算取消 → 下游仍運行 → 傳遞取消並清理資源。
- 所有錯誤重試 → 負載放大 → 依語義、預算與上限重試。
- 寫入超時換新 ID → 可能重複副作用 → 查權威狀態或補償。
- 串流重連從頭重播 → 重複訊息 → 使用游標、序號與冪等消費。
- 只記最終錯誤 → 無法定位預算耗盡層 → 記錄剩餘時間與 attempt。
追問及應對
追問一:子服務可以延長父 deadline 嗎?
不可以。若需要更長操作,改成非同步任務並返回操作 ID,不要偷偷延長同步請求。
追問二:取消能撤銷已提交交易嗎?
不能保證。取消只能阻止尚未執行工作;已提交交易要查詢、補償或對帳,介面必須暴露狀態。
追問三:如何避免雙重重試?
指定單一重試責任方,其他層只傳播錯誤;統一 attempt 和總預算,監控每層放大倍數。
追問四:串流沒有訊息時是否取消?
依產品語義區分長連線與閒置,使用總 deadline、閒置超時或心跳。閒置超時應產生可重連狀態,不應無限佔用資源。
追問五:deadline 已過但服務端仍計算怎麼辦?
服務端監聽取消並讓下游可取消;不可取消的計算移到受控背景佇列,記錄操作 ID 與資源上限,避免佔用請求執行緒。