? 一次瀏覽器訪問 www.xxx.com
背后發生了什么?
—— 以及“我點了 ×,數據會不會丟?”的深度剖析
適讀人群:Web 開發者、運維工程師、性能調優/安全從業者
1?? 打開瀏覽器敲下網址:鏈路是如何啟動的?
階段 | 關鍵詞 | 關鍵細節 |
---|---|---|
解析階段 | DNS、緩存 | 瀏覽器先查本地/系統 DNS 緩存 → hosts → 瀏覽器 DNS 緩存 → 遞歸查詢(可能走 DoH/DoT)。拿到 A / AAAA 記錄。 |
連接階段 | TCP 3?way、TLS 1.3 | TCP:SYN → SYN?ACK → ACK;若啟用 QUIC/HTTP?3 則直接 UDP 把 3 次握手+TLS 合并。 TLS:ClientHello→ServerHello→ … → Finished,現代瀏覽器基本啟用 0?RTT/1?RTT。 |
請求階段 | HTTP/2、HPACK、首部壓縮 | 瀏覽器構造 GET / ,附帶頭(UA、Cookie、Accept?Encoding…),優先用 HTTP/2 多路復用并發流。 |
服務器內部 | 負載均衡、反向代理、應用層 | 可能經歷 ELB/Nginx/Sidecar,再到業務容器;中間會有 隊列、限流、熔斷。 |
響應階段 | TCP 流/ QUIC 流 | 服務器寫回狀態行+頭+Body;瀏覽器解析 HTML 發現靜態資源→并發下載→渲染。 |
結束階段 | FIN、連接復用 | HTTP/1.1的長連接或 H2/H3 的流會保持空閑等待復用;超時或瀏覽器關閉時發送 FIN 。 |
2?? “我等得太慢點了 ×,服務器會怎么想?”
2.1 瀏覽器側:取消動作
- 顯式取消:用戶點擊“停止加載”或關閉 Tab。
瀏覽器對活躍的 TCP/QUIC 流發送RST
(或 HTTP/2RST_STREAM
)。 - 隱式取消:超時時間到、頁面卸載等;瀏覽器會 abort fetch,內核可能直接
close()
套接字。
2.2 網絡層:信號傳遞
協議 | 取消信號 | 服務器感知 |
---|---|---|
TCP | RST 包 | 立即終止連接;內核返回 ECONNRESET ,應用讀/寫觸發異常。 |
HTTP/2 | RST_STREAM (error?code=0) | 僅關閉對應流,復用的其他流不受影響;h2 服務器收到回調。 |
QUIC/HTTP?3 | STOP_SENDING / RESET_STREAM | 與 h2 類似,但基于 UDP;更快釋放流。 |
2.3 服務器側:請求已經“進廚房”怎么辦?
-
剛到負載均衡
- 連接被復用,RST 直接丟棄,Nginx 可能還沒把請求轉給應用,成本小。
-
已進應用但未執行業務
- 線程/協程池檢測到 socket 斷開會拋
IOException
;大多數框架(Spring MVC、Express、Gin…)會捕獲記錄ClientAbortException
后結束。
- 線程/協程池檢測到 socket 斷開會拋
-
業務正在執行
- CPU 正燒:圖片壓縮、大 SQL、RPC…
- 同步模型:直到代碼寫響應時才發現對端關閉 → 計算白做了,但數據未寫出,資源浪費。
- 異步/事件模型:中途可檢測
connectionAborted
flag 主動中止。
-
下游已持久化
- 例如下單接口:一旦寫入數據庫/消息隊列,就算用戶取消,也已成功。
- 于是就有了 支付回調、“查看訂單”按鈕來補償交互的不確定性。
3?? “數據丟不丟?”—— 三個不同層次的回答
角度 | 是否丟失? | 解釋 |
---|---|---|
傳輸層 | ? 丟 | 瀏覽器取消后,剩余報文不再發送;未到達的響應也不再接收。 |
應用層 | ? 不一定 | 服務器可能已完整處理,持久化成功。僅“用戶沒看到”而已。 |
業務語義 | 需要冪等 | 好的接口要做到 冪等&可重試:PUT/DELETE 設計有冪等鍵,POST 返回可查詢的 operationId 。 |
4?? 工程實踐:讓“取消”更友好
-
前端
AbortController
取消 fetch;- 超時反饋:Skeleton Screen / 旋轉骨架;
- 使用 web?worker + keep?alive 提升體驗。
-
網關/反代
- 配置 proxy_request_buffering off (Nginx) 讓大上傳即時回源;
- 針對
client_abort
及時丟棄后端響應。
-
后端
- 設計 可中斷任務:定期檢查
Thread.currentThread().isInterrupted()
; - 重型計算放入 作業隊列,前端輪詢
jobId
; - 數據庫寫操作加 事務/唯一約束 保證冪等。
- 設計 可中斷任務:定期檢查
-
監控
- 關注
499 Client Closed Request
(Nginx)、444 No Response
; - 鏈路追蹤標記
aborted=true
,評估浪費。
- 關注
5?? 小結
- 用戶取消請求后,網絡傳輸層面數據一定中斷;
- 服務器邏輯可能仍在繼續,甚至已成功寫庫;
- 工程上通過 冪等、作業分離、連接檢測 將“浪費”降到最低;
- 讓前端與后端都能 優雅應對取消,才是真正的端到端體驗優化。