Skip to content

IN · 网络面试题 ​

网络是前端唯一绕不开的"后端领域"——页面加载慢、接口 502、跨域报错、Cookie 丢了, 桩桩件件最后都要落到协议层。面试官问网络,真正想听的不是"三次握手的报文叫什么", 而是 "你能不能把一次请求的代价拆开、知道瓶颈在哪、知道怎么改"。

所以本页的组织方式是:先给分层模型(骨架)→ 再逐层给高频题 → 最后给排障方法论。 与浏览器直接相关的(HTTP 缓存、CORS、Core Web Vitals)链回 A 域,本页只留要点。

📌 结构:零、一次请求的代价分层 · 一、TCP · 二、HTTP 演进 · 三、HTTPS/TLS · 四、DNS · 五、状态码与方法语义 · 六、同源与跨域 · 七、Cookie 与鉴权 · 八、缓存 · 九、长连接选型 · 十、CDN · 十一、排障方法论 · 十二、移动端对照。


高频 Top 10(不会的话优先补这些) ​

#题目一句答案去哪补
1从输入 URL 到页面展示发生了什么不要背流程,要能拆出每一段的耗时和优化点本章
2为什么是三次握手,两次行不行两次无法确认客户端的接收能力,且历史重复 SYN 会让服务端建错连接1.1
3TIME_WAIT 出现在哪一端、为什么要等主动关闭方;① 保证最后 ACK 可达 ② 让旧报文消散1.3
4HTTP/2 解决了什么、又留下了什么解决应用层队头阻塞,没解决 TCP 层队头阻塞 → 才有 HTTP/32.2
5HTTPS 握手多了几个 RTTTLS 1.2 2 个,TLS 1.3 1 个,QUIC 复用 0 个3.2
6跨域是被谁拦的浏览器拦的——请求发出去了、服务端也回了 2006.2
7Cookie 的 SameSite 三档有什么区别Lax 是现代默认:跨站 GET 导航带、跨站 POST/iframe/XHR 不带7.1
8强缓存与协商缓存怎么选带 hash 的静态资源 → max-age=31536000, immutable;HTML → no-cacheA04
9502 / 504 / 499 分别说明什么502 上游无效响应、504 上游超时、499 客户端先断了5.2
10WebSocket 还是 SSE服务端单向推 → SSE(简单得多);双向/高频/二进制 → WS9.1

零、一条主线:一次请求的代价分层 ​

这一节是全页的骨架。 能把它讲清楚,上面 10 题基本都能顺出来。 面试官问"输入 URL 之后发生了什么"时,按这个答,比背八股高一个段位。

HTTPS + HTTP/1.1 首次访问一个域名:

阶段动作代价能不能省
① URL 解析补全协议、编码、HSTS 判断(是否在 preload 列表里 → 直接跳 HTTPS)~0—
② 缓存检查强缓存命中 → 直接结束,一次网络请求都没有0 RTT✅ 最优解
③ DNS浏览器缓存 → OS 缓存/hosts → 本地 DNS 递归 → 根/顶级域/权威0~1 RTTdns-prefetch、HTTPDNS、减少域名数
④ TCP 握手SYN → SYN+ACK → ACK1 RTT长连接复用
⑤ TLS 握手协商套件、验证书、交换密钥2 RTT(1.2)/ 1 RTT(1.3)升级 1.3、会话复用、QUIC 0-RTT
⑥ 发送请求请求行 + 头 + body—头部压缩(HPACK/QPACK)、减小 Cookie
⑦ 等响应(TTFB)网络往返 + 服务端处理 + 可能的回源≥1 RTTCDN、缓存、后端优化
⑧ 收响应受慢启动与带宽限制视包体压缩、分片、减少体积

首屏关键路径的 RTT 账:

HTTPS/1.1 + TLS1.2 首次:  DNS 1 + TCP 1 + TLS 2 + 请求 1  ≈ 5 RTT
HTTPS/2   + TLS1.3 首次:  DNS 1 + TCP 1 + TLS 1 + 请求 1  ≈ 4 RTT
HTTPS/2   + TLS1.3 复用:  0     + 0     + 0     + 请求 1  =  1 RTT
HTTP/3    + QUIC   复用:  0     + 0     + 0     + 请求 1  =  1 RTT(且无队头阻塞)

结论(也是优化口诀):能不请求就不请求(缓存)→ 能复用连接就复用(keep-alive/HTTP2)→ 能就近就就近(CDN)→ 最后才是压缩体积。

🇨🇳 跨地域访问一个美国源站,单程 ~100ms,1 RTT = 200ms。上面从 5 RTT 降到 1 RTT,就是 800ms 的差距—— 这比你在 JS 里抠 100ms 有意义得多。

→ 浏览器侧的性能度量与诊断见 A25,优化清单见 A10。


一、TCP ​

1.1 三次握手:为什么不多不少三次 ​

C → S: SYN     seq=x                (我在,我的初始序号是 x)
S → C: SYN+ACK seq=y, ack=x+1       (我在,我收到你的 x 了)
C → S: ACK     ack=y+1              (我收到你的 y 了,可以发了)

为什么两次不行——两个理由,缺一不可:

  1. 双向确认收发能力:TCP 是全双工,双方都要确认"对方的发送能力 + 自己的接收能力"都对得上。 两次握手只确认了 C→S 方向,服务端不知道客户端能不能收。
  2. 防止历史重复连接:网络里可能有个滞留的旧 SYN 迟到。两次握手下,服务端收到旧 SYN 就直接建连并等数据, 客户端根本不认这个连接 → 白白浪费一个连接资源,还可能让服务端误以为有数据要收。

为什么四次没必要:服务端的 SYN 和 ACK 可以在同一个报文里合并发出(捎带),所以三次就够。

追问:初始序号(ISN)为什么是随机的? → 防止攻击者伪造合法序号注入数据,也避免新旧连接的报文序号重叠。SYN Flood 攻击就是利用半连接队列: 服务端发完 SYN+ACK 后连接处于 SYN_RECV,占着队列等 ACK;伪造源 IP 狂发 SYN 就能把队列打满。 防御:tcp_syncookies(不分配资源,用 cookie 校验真客户端)、加大 somaxconn、云厂商的 SYN 代理/清洗。

1.2 四次挥手:为什么是四次 ​

TCP 是全双工,每个方向要单独关闭:

主动方 → 被动方: FIN          (我这边没数据发了)
被动方 → 主动方: ACK          (知道了)
        …… 被动方可能还有数据要发 ……
被动方 → 主动方: FIN          (我也发完了)
主动方 → 被动方: ACK          (知道了)→ 进入 TIME_WAIT

为什么不能像握手一样合并成三次:被动方收到 FIN 时可能还有数据没发完, 所以 ACK 必须立刻回(否则主动方会重传 FIN),而 FIN 要等数据发完才能发。这就是四次的根本原因。

1.3 TIME_WAIT 与 CLOSE_WAIT(面试与排障双高频) ​

TIME_WAIT 只出现在主动关闭方,持续 2MSL(RFC 793 建议 MSL=2 分钟,Linux 实际按 60s 计时)。

两个存在的理由:

理由说明
可靠终止最后一个 ACK 可能丢,被动方会重发 FIN。没有 TIME_WAIT 的话,主动方已经关闭,只能回 RST,被动方就会以错误方式结束
旧报文消散等 2MSL 让这次连接的残留报文在网络中消失,否则四元组相同的新连接可能收到旧报文,数据串台

面试追问:服务器上 TIME_WAIT 几万个,是不是出事了?

先区分两种状态,结论完全相反:

状态谁的问题判断
CLOSE_WAIT 大量堆积应用 bug对方已经发 FIN 了,你的程序没调 close()。去查代码(连接池没归还、异常分支没关流)
TIME_WAIT 大量堆积通常无害只占一点内存和端口表项。是协议设计的一部分,不是泄漏

TIME_WAIT 真正会出问题的场景:客户端/压测机端口耗尽(ip_local_port_range 用光 → Cannot assign requested address)、 或短连接高频打同一个 LB(四元组复用压力大)。

处理手段与代价(按推荐度排序):

手段说明代价 / 坑
让客户端主动关闭服务端不主动断,把 TIME_WAIT 甩给客户端最干净,首选
长连接 / 连接池复用连接,根本上减少关闭次数需要调 idle 超时(见 10.4)
net.ipv4.tcp_tw_reuse=1复用处于 TIME_WAIT 的出向连接(依赖 TCP 时间戳 PAWS)对服务端入向连接作用有限
tcp_tw_recycle不要提它当方案NAT 后时间戳乱序会大面积丢连接,Linux 4.12 已移除
SO_LINGER=0直接发 RST 不等可能丢数据、绕过优雅关闭,只在明确可接受时用

1.4 拥塞控制:为什么新连接总是"先慢后快" ​

  • 慢启动:新连接的拥塞窗口 cwnd 从一个很小的值(现代 Linux initcwnd = 10 个 MSS)开始, 每收到一轮 ACK 就翻倍 → 前几个 RTT 很慢。
  • 丢包 → 快速重传 / 快速恢复(CUBIC 等);BBR 则是基于带宽与 RTT 建模,不去等丢包,弱网长肥管道收益明显。

对前端的意义: ① 复用连接(HTTP/2 多路复用、keep-alive)不只是省握手,还跳过慢启动; ② 首屏关键资源尽量小且早——慢启动阶段窗口小,大文件在开头几 RTT 就是传不动。


二、HTTP 演进 ​

2.1 一条脉络 ​

版本核心改进遗留问题
HTTP/1.0短连接,每请求一次握手建连成本极高
HTTP/1.1长连接 keep-alive、管线化、Host 头、缓存字段、Range应用层队头阻塞;头部无压缩、重复传
HTTP/2二进制分帧、多路复用、HPACK 头部压缩、流优先级、(服务端推送,已废弃)TCP 层队头阻塞
HTTP/3(QUIC)基于 UDP,流之间独立重传、0/1-RTT 建连、连接迁移、加密内置UDP 被中间设备限流/阻断;运维可观测性弱

2.2 HTTP/2 解决了什么,代价是什么 ​

解决:应用层队头阻塞(HOL)。HTTP/1.1 下一个 TCP 连接上请求-响应必须串行, 浏览器只能靠同域 6 个并发连接缓解(所以以前有"域名分片"这种优化)。

代价(这是加分点):

  • HTTP/2 通常只用 1 个 TCP 连接。一旦丢包,TCP 的重传会让所有流一起等—— 在高丢包弱网下,HTTP/2 可能比 HTTP/1.1 的 6 个连接更慢。
  • 流优先级在浏览器/服务器实现上普遍不理想(Chrome 多次调整策略,服务端推送已被主流浏览器移除)。
  • 中间件(老代理、WAF)对 h2 支持不一致 → 上线要灰度、看错误率。

前端实践结论:

优化手段HTTP/1.1 时代HTTP/2 时代
域名分片(多域名)✅ 提升并发❌ 负优化(多 DNS + 多 TLS + 多慢启动)
资源合并(雪碧图、大 bundle)✅ 减少请求⚠️ 要重新权衡(细粒度更利于缓存与按需)
内联小资源✅ 省请求⚠️ 不能再被单独缓存
压缩 / 内容 hash / CDN✅✅ 依然有效

2.3 HTTP/3 与 QUIC 的关键点 ​

  • 独立流:每个 stream 单独重传,丢一个包不阻塞其他流 → 弱网收益最大(移动端、跨境)。
  • 0-RTT:复用之前连接的参数(PSK)直接发数据。⚠️ 0-RTT 数据可被重放—— 只应承载幂等请求,或做应用层抗重放(一次性 token)。
  • 连接迁移:用 Connection ID 而非四元组标识连接,手机从 WiFi 切到 4G 不断线——这是它在移动端最实在的价值。
  • 代价:QUIC 把传输层也加密了,中间盒看不见 → 依赖 CDN/网关支持;部分企业网封 UDP 会直接不可用; 排障工具链不如 TCP 成熟。启用策略:HTTP/2 与 h3 同时通告(Alt-Svc),客户端协商,降级无感。

三、HTTPS / TLS ​

3.1 三个目标与两套加密 ​

目标手段
机密性握手后用对称加密(AES-GCM / ChaCha20-Poly1305)传数据
完整性AEAD 自带认证标签(或 MAC)
身份认证证书链验证(非对称签名)

为什么不全用非对称加密:性能差几个数量级。非对称只用来安全地协商出对称密钥。

3.2 握手流程与 RTT ​

TLS 1.2(2 RTT):

C → S: ClientHello      (随机数、支持的套件列表、SNI 域名)
S → C: ServerHello + Certificate + ServerKeyExchange  (选定套件、证书链)
C → S: 验证书 → PreMasterKey(用服务端公钥加密)→ ChangeCipherSpec + Finished
S → C: ChangeCipherSpec + Finished

TLS 1.3(1 RTT):ClientHello 直接带上 key_share(猜一个套件先算好密钥), 服务端回 ServerHello 就能开始加密 → 省 1 个 RTT。同时砍掉了:RSA 密钥交换、静态 DH(无前向安全)、 RC4/SHA1/CBC 等旧套件、压缩(CRIME)。

前向安全(PFS):用临时 ECDHE,即使服务器私钥将来泄露,历史流量也解不开。 TLS 1.3 强制 ECDHE,1.2 需要正确配置套件才具备。

3.3 证书:校什么,怎么被攻破 ​

浏览器校四件事:签名链(一路验到操作系统/浏览器内置的根证书)、有效期、 域名匹配(看 SAN,不是 CN)、吊销状态(CRL / OCSP / OCSP Stapling,现代浏览器多用自家推送的吊销列表)。

攻击 / 场景原理防御
中间人(MITM)在客户端装一个受信任的根证书,就能伪造任意站点用户侧别乱装根证书;App 侧做证书锁定(pinning);服务端上 HSTS
CA 误签发 / 被入侵拿到某域名的合法证书证书透明(CT):所有证书进公开日志,浏览器要求带 SCT
混合内容HTTPS 页面里加载 http:// 资源主动内容(脚本/iframe)被直接阻断;用 upgrade-insecure-requests
抓包调试Charles / whistle / mitmproxy 就是一次"合法的中间人"装根证书 + App 开启可调试 pinning

SNI 的隐私洞:为了一个 IP 承载多域名,ClientHello 里明文带域名 → 链路上的观察者能看到你访问哪个站。 解决方案是 ECH(Encrypted Client Hello),正在逐步推进。


四、DNS ​

4.1 解析过程 ​

浏览器缓存 → 操作系统缓存 + hosts → 本地 DNS(递归解析器,运营商/公司)
                                        ↓ 迭代查询
                              根服务器 → 顶级域(.com)→ 权威服务器(example.com)
  • 递归 vs 迭代:你到本地 DNS 是递归(它替你跑完),本地 DNS 到各级服务器是迭代(自己问下一级)。
  • TTL 决定各级缓存多久。换 IP 前要提前把 TTL 调小(比如先降到 60s),否则会出现"改了 DNS 半天不生效"。

4.2 常见记录与坑 ​

记录用途坑
A / AAAAIPv4 / IPv6 地址双栈下优先 AAAA 会有回退等待(Happy Eyeballs)
CNAME别名(CDN 就是 CNAME 到调度域名)同名不能有其他记录;链太长会多几次解析;不能用在根域名(用 ALIAS/ANAME 代替)
NS / SOA委派与区信息—
MX / TXT邮件 / SPF、域名验证—
CAA限制哪些 CA 能给你签发配了却没包含你用的 CA → 签发失败

4.3 前端能做的三件事 ​

  1. 减少域名数:HTTP/2 之后,多一个域名 = 多一次 DNS + TCP + TLS。域名分片已成历史。
  2. <link rel="dns-prefetch">:只做 DNS 预解析,很便宜,可以放宽用。
  3. <link rel="preconnect">:DNS + TCP + TLS,贵(占连接、抢带宽),控制在 3~4 个, 只给确定要用的关键域名(API 域、CDN 域、字体域)。

4.4 劫持与污染 ​

表现:返回错误 IP(广告页、缓存服务器)、或干脆解析失败。传统 Local DNS 是重灾区。

方案说明适用
HTTPDNS不走 53 端口,直接 HTTP 请求解析服务拿 IPApp 内最常用;Web 端拿不到(浏览器发请求时已经要解析了)
DoH / DoTDNS over HTTPS/TLS,加密防篡改系统与浏览器层面,需环境支持
换公共 DNS8.8.8.8 / 1.1.1.1 / 223.5.5.5简单,但解决不了基于出口的劫持,且可能让 CDN 调度不准(Local DNS 定位错)

五、状态码与方法语义 ​

5.1 必须能脱口而出的码 ​

码含义面试 / 实战要点
101Switching ProtocolsWebSocket / HTTP 升级成功
103Early Hints先把 Link: rel=preload 给浏览器再出正文,能提前几十~几百 ms 开始下载
204No Content不能有 body;DELETE 常用;fetch 解析 JSON 会报错,要特判
206Partial Content配合 Range / Accept-Ranges:断点续传、视频拖动、大文件分片下载
301 / 302永久 / 临时重定向⚠️ 301 会被浏览器长期缓存,改不回来。HTTP→HTTPS 建议 301,但要知道"后悔成本高"
307 / 308保持方法 + 保持 body 的重定向302/303 历史上会把 POST 降级成 GET;要保住方法就用 307/308
304Not Modified协商缓存命中,没有 body
401 vs 403未认证 vs 已认证但无权限401 必须带 WWW-Authenticate;别把"没登录"写成 403
409Conflict版本冲突/乐观锁失败,比用 400 更准确
429Too Many Requests应带 Retry-After;前端要做指数退避 + 抖动,别整齐地一起重试(放大雪崩)
502Bad Gateway网关拿到了上游的无效响应(上游崩了 / reset)
503Service Unavailable上游不可用,通常可重试(带 Retry-After)
504Gateway Timeout上游超时了
499(Nginx 私有)客户端提前断开常见原因:前端超时设太短、用户切走页面、弱网

5.2 5xx 与排障对应关系 ​

502 → 上游进程挂了 / 重启中 / 返回了非法报文 → 看上游日志与发布记录
504 → 上游慢或网关超时配太短                → 看慢查询、回源链路、超时配置
499 → 客户端先走了                          → 看前端超时上报、用户行为(切页/切后台)
503 → 主动限流 / 熔断 / 无可用实例           → 看限流配置与容量

5.3 幂等:重试能不能乱来 ​

方法安全幂等说明
GET / HEAD✅✅不应改变服务端状态;GET 会被缓存,所以加版本号/时间戳
PUT / DELETE❌✅用完整资源替换 / 删除,重复执行结果一致
POST❌❌每次都可能产生新资源
PATCH❌❌(部分更新语义上不保证)可用条件请求(If-Match + ETag)做成幂等

为什么重要:网络超时时你不知道服务端执行了没有。此时能否重试,取决于幂等。 POST 要做幂等,就引入 Idempotency-Key(客户端生成,服务端去重,保留首次结果)。


六、同源与跨域 ​

6.1 同源策略到底限制了什么 ​

同源 = scheme + host + port 三者全等(https://a.com:443 与 http://a.com:80 不同源)。

它限制三类东西:DOM 访问(跨域 iframe 的 contentDocument)、 数据读取(XHR/fetch 响应、Canvas 像素、localStorage)、部分脚本行为。

6.2 一个必须先讲清的误区 ​

CORS 报错 ≠ 请求没发出去。 请求发出去了,服务端也处理了、也返回了 200,是浏览器拿到响应后按 CORS 头判断"不给你看",把结果丢掉了。

排障意义:看到控制台 CORS 报错,别只顾着改前端—— 先看 Network 里那个请求到底有没有返回、返回头里有没有 Access-Control-Allow-Origin。 如果是 OPTIONS 预检失败,真正的请求根本没发出去,这时候才是服务端没配好。

6.3 简单请求 vs 预检 ​

简单请求(不触发预检)必须同时满足:

  1. 方法是 GET / HEAD / POST;
  2. 请求头只包含 CORS 安全列表(Accept、Accept-Language、Content-Language、 Content-Type 且值仅限 application/x-www-form-urlencoded / multipart/form-data / text/plain、Range …);
  3. 未使用 ReadableStream 等特殊 API。

→ 换句话说:Content-Type: application/json 的 POST 一定触发预检。

预检(OPTIONS) 会多一个 RTT。优化手段: Access-Control-Max-Age 缓存预检结果(浏览器有上限,Chrome 约 600s); 或把接口改成简单请求(如 urlencoded);或根本别跨域——用同源代理 / BFF。

三方条件缺一不可:

http
Access-Control-Allow-Origin: https://your-site.com   ← 不能是 *
Access-Control-Allow-Credentials: true
js
fetch(url, { credentials: 'include' })   // 或 axios withCredentials: true

6.5 跨域方案怎么选 ​

方案适用评价
CORS前后端分离、有控制权正解,唯一标准方案
同源代理开发期 devServer.proxy;生产 Nginx / BFF 转发最省心,把跨域消灭在架构层;还能顺便聚合接口、隐藏 token
postMessageiframe 间通信必须校验 event.origin
JSONP只有 GET 的老接口❌ 已淘汰,有 XSS 风险
document.domain同父域❌ 已废弃

→ 前端安全相关的完整防御见 A08;BFF 见 A23。


七、Cookie 与鉴权 ​

属性作用面试点
HttpOnly禁止 JS 读取防 XSS 窃取会话。注意它不防 XSS 本身,只防"偷走 token"
Secure仅 HTTPS 传输明文站点下不会发送
SameSite=Strict完全禁止跨站携带从外链点进来不带 Cookie(会显示未登录,用户体验差)
SameSite=Lax现代浏览器默认跨站顶级导航的 GET 带,跨站 POST / iframe / XHR 不带 → 天然挡掉大部分 CSRF
SameSite=None允许跨站携带必须同时配 Secure,否则被拒
Domain / Path作用域Domain 不能随意设成父域以上;不设则仅当前 host
__Host- 前缀锁 host、必须 Secure、不能设 Domain防子域覆盖会话 Cookie,安全加固手段
Partitioned(CHIPS)按顶级站点分区存储第三方嵌入场景下的隔离方案

容量:单条 Cookie 约 4KB,每域约 50 条(各浏览器有差异)。 结论:Cookie 只放会话标识,别塞业务数据——它会无条件跟着每个请求发出去,白白吃掉上行带宽。

7.2 Token 存哪(高频争议题) ​

方案主要风险缓解
localStorageXSS 直接读走严格 CSP + 依赖扫描 + 不引入不可信脚本;短期 token
Cookie + HttpOnly + SameSiteCSRFSameSite=Lax + 双重提交 token / 校验 Origin 头
内存(变量)+ Refresh Token 在 HttpOnly Cookie刷新页面即失效较优解:access token 存内存,refresh token 存 Cookie

Refresh Token 工程要点:轮换(rotation)——每次刷新都发新的 refresh token 并作废旧; 若检测到旧 token 被重用,说明可能泄露,直接吊销整条 token 家族。

→ 完整讨论(含 CSP、XSS/CSRF 攻防代码)见 A08。


八、缓存 ​

本页只给决策要点,完整机制(决策流程、配置三档、常见误区)见 A04 · 浏览器与网络。

两套机制:

类型字段是否发请求
强缓存Cache-Control: max-age / Expires❌ 不发,200 (from disk cache)
协商缓存ETag/If-None-Match(优先)、Last-Modified/If-Modified-Since✅ 发,可能回 304(无 body)

决策口诀:

资源配置
带内容 hash 的 JS/CSS/图片Cache-Control: public, max-age=31536000, immutable
index.html(入口)Cache-Control: no-cache(每次协商)或 很短 max-age + 发版主动刷 CDN
个性化接口响应private, no-store(或 no-cache + ETag)
绝不缓存no-store(注意与 no-cache 的区别:no-cache 是"可以存,但每次要问")

高频追问:

  • 为什么带 hash 的资源可以写一年? 因为内容一变文件名就变,浏览器会去请求新 URL,旧缓存自然失效—— 这比 max-age=0 + 协商更快也更省。
  • 启发式缓存:响应既没有 Cache-Control 也没有 Expires 时,浏览器可能按 Last-Modified 推一个 (常见为 (now - Last-Modified) * 10%)缓存时间 → 表现为"没配缓存却缓存了"。
  • Vary 的代价:Vary: User-Agent 会让 CDN 缓存碎片化、命中率暴跌。一般只 Vary: Accept-Encoding。
  • s-maxage / stale-while-revalidate:分别控制共享缓存(CDN)时长、以及"先用旧的再后台更新"。

九、长连接选型 ​

9.1 四种方案横向对比 ​

方案方向实时性实现成本关键代价
短轮询客户端拉差极低大量无效请求;间隔决定延迟
长轮询半推较好中请求挂起占连接;每次收到后要重发
SSE服务端 → 客户端好低(EventSource 自带重连)只能单向、只能 GET、文本;HTTP/1.1 下占一个连接
WebSocket双向最好高要自己做心跳 + 重连退避 + 断线补偿;代理需支持 Upgrade

判断顺序(这样答很加分):

  1. 只需服务端推、数据单向、文本 → SSE。它比 WebSocket 简单一个量级,还自带重连。
  2. 需要客户端频繁上行 / 二进制 / 低延迟双向 → WebSocket。
  3. 更新频率低(分钟级)→ 轮询,别过度设计。
  4. 内部系统、已有网关限制 → 看链路是否支持 Upgrade 与长连接。

9.2 WebSocket 工程要点(面试官会追问"那你怎么保证它可靠") ​

  • 握手:HTTP Upgrade: websocket → 101 Switching Protocols。浏览器 API 不能自定义请求头, token 通常走 query / subprotocol / 首个消息;注意别把 token 写进容易被记录的 URL 日志。
  • 心跳:TCP 有 keepalive 但默认时间太长(小时级),且中间代理可能静默掐断空闲连接 → 应用层必须自己 ping/pong。
  • 重连:指数退避 + 随机抖动(避免服务端恢复瞬间被全体客户端打爆),并限制最大重试次数与降级提示。
  • 断线补偿:重连后断连期间的消息已经丢了 → 必须设计补偿(客户端带上一次收到的序号/游标,服务端补发增量,或客户端主动拉全量快照)。 这是"会用 WS"和"能把 WS 上生产"的分水岭。
  • 鉴权与超时:握手时鉴权;服务端 idle timeout 要大于客户端心跳间隔。
  • 压缩:permessage-deflate 能省带宽,但对敏感数据有类 CRIME/BREACH 的侧信道风险。
  • 多标签页:每个标签开一条连接会放大服务端压力 → 用 SharedWorker 或主标签页选举实现单连接共享。

十、CDN ​

10.1 它在哪一层起作用 ​

用户 → 边缘节点(POP)→ (命中直接返回)
                      → (未命中)回源 → 源站

三类能力:静态加速(边缘缓存)、动态加速(回源链路优化:长连接复用、最优路径、协议优化)、 边缘计算(Edge Functions:在边缘做鉴权、A/B、重定向、改写响应——把逻辑搬到离用户最近的地方)。

10.2 前端必须配对的几件事 ​

事说明
内容 hash + 长缓存资源名带 hash,CDN 可长期缓存;发版 = 换新 URL,无需刷新
HTML 别被 CDN 缓存住否则发版用户拿到的还是旧入口 → HTML 用 no-cache 或发版时主动刷新
缓存键要写对默认常按 URL;带 query 的接口/资源要明确"是否忽略参数",否则命中率惨不忍睹
CORS 与字体跨域字体必须带 CORS 头,否则被浏览器拒绝
图片处理用 CDN 的缩放 / WebP-AVIF 协商(按 Accept),别让用户下原图
Vary谨慎使用,会造成缓存碎片化

10.3 常见坑 ​

  • 发版不生效:HTML 被缓存 / 回源缓存没刷 → 检查 Age 与 X-Cache 响应头。
  • 命中率低:URL 带随机参数、Vary: Cookie/User-Agent、缓存键包含无关 header。
  • 缓存了个性化响应:带 Set-Cookie 或用户信息的响应被边缘缓存 → 用 private / no-store,或按登录态分流。
  • 回源压力:缓存时间太短 + 大量未命中 → 加 stale-while-revalidate / stale-if-error 抗雪崩。

10.4 经典坑:502 与 keepalive 超时不匹配 ​

这是"能答出来的都是真干过"的一题。

现象:接口偶发 502,频率很低,重试就成功。 原因:网关(Nginx/CDN)与上游复用长连接,但上游的 idle 超时比网关短—— 上游先关了连接,网关正好拿这个连接发请求 → 收到 RST → 502。

排查与解法:

  1. 保证 网关的 keepalive 空闲超时 < 上游的(让网关主动回收);
  2. 网关侧开启 对幂等请求的重试(proxy_next_upstream error timeout http_502,且只用于 GET/幂等);
  3. 上游关闭 tcp_tw_recycle 之类的激进参数;观察 netstat 里连接状态分布。

十一、排障方法论 ​

11.1 用 Timing 分段定位(第一件事,也是最重要的一件) ​

Chrome DevTools → Network → 单个请求 → Timing:

段长说明什么
Queueing / Stalled排队:同域并发打满(HTTP/1.1 的 6 连接)、代理排队、高优先级资源抢占
DNS LookupDNS 慢 / 域名太多 → dns-prefetch、HTTPDNS、减少域名
Initial connection / SSL建连与 TLS 慢 → 复用连接、升级 TLS 1.3、上 CDN
Request sent通常很短;很长说明请求体巨大或上行差
Waiting (TTFB)服务端处理 + 网络往返 + 回源 → 后端/数据库/CDN 缓存命中率
Content Download包体太大 / 带宽不足 / 慢启动 → 压缩、分片、砍体积

一句话判断法:TTFB 大 → 找后端/回源;Content Download 大 → 找包体; Stalled 大 → 找并发与连接;SSL 大 → 找 TLS 与链路距离。

11.2 命令行分段(服务端/本地通用) ​

bash
curl -o /dev/null -s -w \
"dns:%{time_namelookup} tcp:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total}\n" \
https://example.com/api
  • time_namelookup:DNS
  • time_connect:TCP 建连完成
  • time_appconnect:TLS 完成 → tls - tcp 即握手耗时
  • time_starttransfer:TTFB
  • time_total - time_starttransfer:下载耗时

11.3 抓包与代理 ​

工具层用途
DevTools / whistle / Charles / mitmproxyHTTP(S)看请求头响应头、改包、mock、弱网模拟。HTTPS 需装并信任根证书(本质是一次 MITM)
Wireshark / tcpdumpTCP/IP看重传、RST、零窗口、乱序——"偶发慢/偶发断"这类问题只有这层能定性
openssl s_client / testssl.shTLS看证书链、套件、是否支持 1.3
dig / nslookupDNS看解析结果、TTL、链路

11.4 一张排障对照表 ​

现象优先怀疑验证
首屏特别慢,但接口都很快关键链路 RTT 多 / 资源阻塞看瀑布图:是否有串行依赖链;DNS/TLS 段占比
接口偶发 502上游重启 / keepalive 不匹配(见 10.4)网关错误日志 + 连接状态分布
接口大面积 504上游慢 / 超时配太短对比上游耗时 P99 与网关超时
控制台 CORS 报错响应头缺失(不是请求没发)Network 看真实响应 + OPTIONS 是否被拦
登录态莫名丢失SameSite / 跨域未带凭据 / 时钟 / 域名不一致看 Set-Cookie 属性与请求是否携带
只有部分用户慢地域 DNS 调度错 / 运营商链路 / CDN 节点按地域与运营商维度聚合;换 DNS 对比
弱网下特别差HTTP/2 单连接队头阻塞 / 慢启动试试 1.1 多连接或 h3 对比;看丢包率

→ 前端侧的可观测性体系(日志/指标/链路追踪/告警)见 A12。


十二、移动端 / Flutter 对照 ​

有 Flutter 背景的话,这部分是差异化优势——大多数纯前端答不出移动网络的坑。

主题Web移动端(Flutter/Dart)
连接复用浏览器自动 keep-alive / h2 多路复用HttpClient 需要自己维护连接池与 idle 超时(Dio 默认已做)
TLS系统信任库 + HSTS可证书锁定(pinning):安全性高,但证书轮换会直接让老版本 App 挂掉,且抓包调试变难 → 建议留可配置的开关
弱网丢包 → h2 队头阻塞QUIC 收益更大(连接迁移:WiFi↔4G 不断线);可接 Cronet/系统 QUIC 栈
明文流量混合内容被阻断Android 高版本默认禁明文(networkSecurityConfig 显式开),iOS ATS 同理
DNSdns-prefetchApp 内常见 HTTPDNS(绕过运营商 Local DNS 劫持)
平台限制—息屏/切后台会被系统掐连接 → 必须重连 + 断线补偿(同 9.2)
调试DevTools 一把梭需代理 + 装根证书;iOS/Android 上抓 HTTPS 比浏览器麻烦得多

十三、自检清单 ​

协议层

  • [ ] 能画出一次 HTTPS 请求的 RTT 分解,说出每一段能怎么优化
  • [ ] 说清三次握手"为什么不是两次",四次挥手"为什么不是三次"
  • [ ] 说清 TIME_WAIT 属于哪一端、两个存在理由、CLOSE_WAIT 堆积意味着什么
  • [ ] 说清 HTTP/2 解决了什么、没解决什么,以及为什么 HTTP/1.1 的域名分片现在成了负优化
  • [ ] 说清 TLS 1.2 与 1.3 的 RTT 差异,以及 0-RTT 的重放风险
  • [ ] 说清证书校验的四项,以及 MITM / 证书锁定 / HSTS 的关系

应用层

  • [ ] 看到 502/504/499 能立刻说出怀疑方向
  • [ ] 判断一个请求是否触发预检,并知道如何消除它
  • [ ] 说出 SameSite 三档行为差异,能解释"为什么登录态掉了"
  • [ ] 给带 hash 的静态资源 / HTML / 个性化接口各自配正确的 Cache-Control
  • [ ] 在 SSE 与 WebSocket 之间做出有理由的选择,并说清 WS 上线需要补的三件事

排障

  • [ ] 拿到一个慢请求,能按 Timing 分段定位到是 DNS / 建连 / TTFB / 下载
  • [ ] 会用 curl -w 在命令行上复现分段耗时
  • [ ] 遇到"偶发 502"能想到 keepalive 超时不匹配

十四、常见误区 ​

❌ 误区✅ 正解
"跨域是服务端把请求拦了"是浏览器拦的响应;预检失败才是没发出去
"HTTPS 用了非对称加密传数据"非对称只用来协商密钥,数据走对称加密
"TIME_WAIT 多就是泄漏"它是协议设计的一部分;CLOSE_WAIT 多才是代码 bug
"HTTP/2 一定比 1.1 快"高丢包弱网下单连接反而更慢;要实测
"no-cache 就是不缓存"是"可以存,但每次必须验证";不缓存是 no-store
"301/302 差不多"301 被浏览器长期缓存,改不回来;302/303 可能把 POST 降级成 GET
"Cookie 里多存点数据省事"每个请求都会带上它,白白增加上行;还有 4KB 上限
"WebSocket 连上就完事了"没有心跳、退避重连、断线补偿,上线必出事
"加个域名能提升并发"HTTP/2 之后这是负优化(多 DNS + 多 TLS + 多慢启动)
"装了 HTTPS 就安全了"还有混合内容、HSTS、Cookie 属性、CSP 一整套要配

相关文档 ​