返回列表

部落格

我們如何優雅地處理網路中斷

網路隨時都在斷。以下是 Tactic Remote 如何確保你在網路中斷時永遠不會遺失工作或上下文。

發布於 標籤: engineering / reliability / networking
部落格

繼續查看該欄目下的產品與工程更新。

瀏覽此分類

如果你曾經用過一個遠端工具,在短暫的網路異常後顯示一個旋轉圖示然後再也不會恢復,你就能理解為什麼我們對重新連線的可靠性如此執著。在行動開發工具的世界中,網路中斷不是邊界案例——而是常態。手機在 Wi-Fi 和行動網路之間切換。電梯阻隔訊號。咖啡廳網路本質上就是不可靠的。

Tactic Remote 的重新連線引擎圍繞一個原則設計:網路中斷應該盡可能對使用者透明,在無法做到時則優雅地傳達。

狀態機

我們重新連線邏輯的核心是一個具有六個狀態的有限狀態機:

┌──────────┐     connect     ┌───────────┐
│ INITIAL  │───────────────►│ CONNECTED │◄─────────────┐
└──────────┘                 └─────┬─────┘              │
                                   │                    │
                              disconnect           reconnect
                                   │              success │
                              ┌────▼─────┐              │
                              │ UNSTABLE │──── retry ───►│
                              └────┬─────┘    (1-3s)    │
                                   │                    │
                              3 failures           ┌────┴─────┐
                                   │               │RECOVERING│
                              ┌────▼─────┐        └────┬─────┘
                              │RECONNECT │── retry ────►│
                              │  ING     │  (5-30s)     │
                              └────┬─────┘              │
                                   │                    │
                              timeout (5min)            │
                                   │                    │
                              ┌────▼─────┐              │
                              │DISCONNEC │─── manual ───┘
                              │  TED     │   reconnect
                              └──────────┘

CONNECTED:正常運作。資料自由流動。

UNSTABLE:一次連線嘗試失敗,但我們懷疑這是暫時性的。每 1-3 秒重試一次。UI 顯示一個微妙的警告指示器但不打斷使用者。

RECONNECTING:多次失敗表明存在真正的問題。重試間隔指數增長(5 秒、10 秒、20 秒、上限 30 秒)。UI 顯示清晰的「重新連線中」橫幅。

RECOVERING:連線已重新建立,但需要同步狀態。用戶端向伺服器請求狀態差異,套用後轉換到 CONNECTED 狀態,完成協調。

DISCONNECTED:已超過重試逾時時間(預設:5 分鐘)。使用者必須手動觸發重新連線。這可以防止在真正離線的裝置上無限消耗電量。

協調協定

重新連線是容易的。協調——讓用戶端和伺服器重新同步——才是困難的部分。

當 iPhone 在中斷後重新連線時,它會向伺服器發送最後已知的狀態向量:它處理的最後一個終端機更新的序號、工作階段 ID 和時間戳。伺服器使用這些資訊來精確計算用戶端錯過了什麼。

情境 1:短暫中斷(< 30 秒)。伺服器的輸出緩衝區仍然包含所有錯過的更新。它按順序重播這些更新,用戶端依序套用。從使用者的角度來看,終端機在不到一秒內「追上進度」——他們會看到一陣快速捲動,錯過的輸出隨即出現。

情境 2:中等中斷(30 秒 - 5 分鐘)。輸出緩衝區可能已經部分輪轉。伺服器發送部分重播加上完整的終端機快照。用戶端套用重播後,與快照進行驗證以確保一致性。

情境 3:長時間中斷(> 5 分鐘)。伺服器僅發送完整快照。用戶端替換其整個狀態。這是效率最低的路徑,但無論錯過了多少內容都能保證正確性。

type ReconciliationResponse = {
  method: 'replay' | 'replay_and_snapshot' | 'snapshot'
  replay?: TerminalUpdate[]
  snapshot?: TerminalSnapshot
  sessions: SessionState[]
  pendingApprovals: ApprovalRequest[]
}

請注意協調也包含了 pendingApprovals。如果 Claude Code 在手機離線時請求了核准,該請求會作為重新連線的一部分被傳遞。這至關重要——如果沒有它,Claude Code 會一直卡在等待使用者從未看到的核准。

差點把我們搞壞的邊界案例

核准途中斷線

使用者收到核准通知,點擊「核准」,然後在核准送達伺服器之前失去連線。操作會被執行嗎?

我們的解決方案:核准使用確認協定。伺服器在收到用戶端的核准訊息並向用戶端確認收到後,才會執行被核准的操作。如果連線在核准過程中中斷,核准會在重新連線時重新傳送。重複的核准是冪等的——發送兩次「核准請求 #47」與發送一次效果相同。

離線期間工作階段結束

你的 Claude Code 任務在手機離線時完成。當你重新連線時,工作階段處於已終止狀態。用戶端應該顯示最終輸出嗎?完成通知嗎?還是兩者都顯示?

我們透過在協調回應中包含 sessionEndState 來處理這個問題。用戶端渲染最終的終端機狀態,並顯示一個「任務在離線期間完成」的橫幅及發生事情的摘要。使用者立即看到結果,而非盯著過時的進行中輸出。

Mac 重啟的情況

最具挑戰的情境:你的 Mac 在手機離線時重啟(由於系統更新、電源故障或當機)。當手機重新連線時,伺服器對之前的工作階段毫無記憶。

我們的解決方案:Mac 配套應用程式將工作階段的中繼資料(工作階段 ID、專案路徑、最後已知狀態)持久化到磁碟。重啟時,它讀取這些中繼資料,並在可能的情況下重新附加到存活的 tmux 工作階段。協調協定透過伺服器紀元計數器偵測伺服器重啟,並啟動完整的狀態重建,而非嘗試增量協調。

效能特性

我們透過三個指標衡量重新連線的效能:

視覺恢復時間 — 從網路恢復到使用者看到當前終端機狀態需要多久。我們的 P95 在區域網路為 1.8 秒,Cloudflare Tunnel 為 3.2 秒。

資料效率 — 協調過程中傳輸的資料量與完全重新整理的比較。對於 60 秒以內的中斷,我們的協調協定比完全重新整理少傳輸 85% 的資料。

正確性 — 對狀態不一致零容忍。我們執行持續整合測試,模擬數千次隨機時間的斷線/重新連線循環,並驗證最終用戶端狀態與伺服器狀態逐位元組匹配。

給建構者的經驗

如果你正在建構任何類型的即時行動應用程式,我們的經驗建議三個原則:

  1. 從第一天就為斷線設計。 在一個為永遠連線設計的系統上添加重新連線功能,比從一開始就將間歇性連線作為第一級關注點來建構要困難好幾個數量級。

  2. 冪等性是你最好的朋友。 每個用戶端到伺服器的訊息都應該可以安全重播。這單一特性就能消除整個類別的重新連線錯誤。

  3. 用真實網路測試,而非模擬器。 網路條件模擬器無法重現真實世界故障的全部多樣性——部分封包傳遞、DNS 解析延遲、TLS 握手逾時。我們在實際的行動網路、電梯中和擁擠的會議 Wi-Fi 上進行測試。

可靠的重新連線不是光鮮亮麗的工作。使用者在它正常運作時不會讚美它——他們只是不會注意到中斷。但當它失敗時,信任會瞬間蒸發。我們認為這項投資是值得的,而使用者願意依賴 Tactic Remote 處理正式環境工作流程也表明他們同意這一點。

立即體驗 Tactic Remote

用手機遠端操作程式開發 Agent

連線到 Mac、Windows 或 Linux 電腦上的 Claude Code、Codex 等 Agent,在 iPhone 或 iPad 上查看進度並傳送下一個請求。