
一次典型的 HTTPS 请求,大致经过这些步骤:
1. URL 解析。 拆出协议、主机、端口、路径、查询参数。浏览器判断是否命中 HSTS、是否有本地缓存。
2. DNS 解析。 把域名转成 IP。依次查浏览器缓存、系统缓存、hosts、本地 DNS、根/顶级/权威 DNS。这一步的耗时经常被低估。
3. TCP 三次握手。 客户端和服务器建立连接,SYN → SYN-ACK → ACK。多一个 RTT。
4. TLS 握手。 协商版本、套件、证书验证、密钥交换。TLS 1.3 把这一步优化到 1-RTT,甚至支持 0-RTT 恢复。
5. 发送 HTTP 请求。 请求行、请求头、请求体。可能带上 Cookie、认证信息、缓存校验头。
6. 服务器处理并响应。 状态码、响应头、响应体。可能经过网关、负载均衡、应用服务、数据库。
7. 浏览器解析渲染。 解析 HTML、加载子资源、构建 DOM/CSSOM、布局、绘制。子资源加载又会重复上述部分步骤。
8. 连接复用或关闭。 HTTP/1.1 默认 keep-alive,HTTP/2 多路复用,HTTP/3 基于 QUIC。
理解这条链路的意义在于:每一环都可能成为瓶颈,而不同环节的排查手段完全不同。DNS 慢和 TLS 慢,看起来都是“打开慢”,但解法天差地别。
HTTP/1.1 是目前仍广泛使用的基础版本。它的核心是“请求-响应”模型:客户端发一个请求,服务器回一个响应,文本协议,人类可读。
几个关键机制:
持久连接(keep-alive)。 一个 TCP 连接可以复用多次请求,减少握手开销。
管道化(pipelining)。 理论上可以连续发多个请求不等响应,但实际因队头阻塞问题,浏览器基本禁用。
缓存机制。 Cache-Control、ETag、Last-Modified、Expires,配合 If-None-Match、If-Modified-Since 做协商缓存。
分块传输。 Transfer-Encoding: chunked 支持流式响应,不必预先知道内容长度。
HTTP/1.1 的核心问题是队头阻塞:同一个连接上,前一个响应没回来,后面的请求就得等。浏览器靠开多个并发连接缓解,但连接数有限,且每个连接都要独立握手,开销大。
HTTP/2 的核心改进是二进制分帧 + 多路复用。它把请求和响应拆成帧,在同一个 TCP 连接上交错传输,多个请求并行,互不阻塞(在应用层)。
主要特性:
多路复用。 一个连接上并发多个流,用 stream id 区分。解决了 HTTP/1.1 的队头阻塞(应用层)。
头部压缩(HPACK)。 请求头往往重复且冗长,HPACK 用静态表和动态表压缩,显著减少开销。
服务器推送。 服务器可以主动推送资源,但实际效果不佳,现已逐渐弃用。
流优先级。 客户端可以标记流的优先级,但各实现支持不一。
HTTP/2 仍然跑在 TCP 上,所以TCP 层的队头阻塞依然存在:一个 TCP 包丢了,后面所有流都要等重传。这是 HTTP/3 要解决的问题。
HTTP/3 不再基于 TCP,而是基于 QUIC(跑在 UDP 之上)。QUIC 在用户态实现了可靠传输、拥塞控制、加密和流控。
关键改进:
消除 TCP 队头阻塞。 QUIC 的流是独立的,一个流丢包不影响其他流。
更快的握手。 QUIC 把传输握手和 TLS 1.3 合并,首次连接 1-RTT,恢复连接可 0-RTT。
连接迁移。 用 connection id 标识连接,而不是 IP+端口。手机从 Wi-Fi 切到 4G,连接可以不断。
内置加密。 QUIC 默认加密,包括头部。
代价是:UDP 在某些网络环境会被限制,调试工具和中间设备支持不如 TCP 成熟。但对移动端和高延迟网络,收益明显。
TCP。 三次握手建立连接,四次挥手关闭。拥塞控制(慢启动、拥塞避免、快速重传)直接影响传输速度。TCP_NODELAY、keep-alive 参数、TIME_WAIT 状态,都是线上常见调优点。
TLS。 负责加密和身份验证。TLS 1.3 精简了握手、移除了不安全套件、支持前向安全。证书链、SNI、OCSP、HSTS 是常见故障点。证书过期、链不完整、域名不匹配,都会导致连接失败。
DNS。 域名到 IP 的映射。TTL 决定缓存时长,CNAME 影响解析链路,EDNS Client Subnet 影响 CDN 调度。DNS 解析慢或解析错误,会导致“网站打不开”但“IP 能通”的经典现象。
这三层的共同特点是:平时无感,出问题就是硬故障。
WebSocket。 通过 HTTP 升级(Upgrade: websocket)建立全双工连接,适合实时推送、聊天、协同编辑。它解决了 HTTP 请求-响应模型不适合服务器主动推送的问题。
SSE(Server-Sent Events)。 基于 HTTP 的单向流式推送,比 WebSocket 简单,适合通知、日志流、AI 流式输出。
gRPC。 基于 HTTP/2 和 Protobuf 的 RPC 框架,适合服务间通信,支持流式、强类型、高效序列化。
选择原则:需要双向实时交互用 WebSocket;只需要服务器推、客户端收用 SSE;内部服务调用追求性能和契约用 gRPC;普通请求响应用 HTTP。
误区一:HTTP/2 一定比 HTTP/1.1 快。 在高丢包网络下,HTTP/2 的 TCP 队头阻塞可能让它更慢。HTTP/3 才是更彻底的解法。
误区二:缓存只靠浏览器。 CDN、反向代理、网关都可能缓存。缓存头和缓存键设计不对,会出现“更新了但用户看到旧内容”。
误区三:跨域是服务器拒绝请求。 CORS 的预检请求(OPTIONS)是浏览器发起的,很多“跨域失败”其实是预检没通过或响应头缺失。
误区四:TLS 只影响安全不影响性能。 握手、加解密、证书验证都有成本。TLS 1.3、会话恢复、OCSP stapling 都是优化手段。
误区五:DNS 不重要。 DNS 解析慢、TTL 设置不当、CNAME 链过长,都会显著影响首屏时间。
误区六:连接池越大越好。 连接数过多会增加服务器负担和握手开销。HTTP/2 下通常一个域名一个连接就够。
误区七:只看状态码不看响应头。 Retry-After、Cache-Control、Location、Set-Cookie 往往包含关键信息。
排查协议问题,最实用的工具往往不是代码,而是 curl。下面几条命令能覆盖大部分场景。
# 看完整握手、请求、响应过程(含 DNS、TCP、TLS)
curl -v https://example.com/
# 只看各阶段耗时:DNS、TCP、TLS、首字节、总时间
curl -w "dns=%{time_namelookup} tcp=%{time_connect} \
tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n" \
-o /dev/null -s https://example.com/
# 强制 HTTP/1.1 或 HTTP/2,对比行为
curl --http1.1 -I https://example.com/
curl --http2 -I https://example.com/
# 查看响应头与缓存相关字段
curl -I https://example.com/这几条命令的价值在于:把“感觉慢”变成“具体哪一阶段慢”。time_namelookup 高说明 DNS 有问题;time_connect 高说明 TCP 握手慢;time_appconnect 高说明 TLS 协商耗时;time_starttransfer 高说明服务器处理慢。定位到阶段,才能对症下药。
如果需要在代码里做健康检查或协议探测,一个最小示例:
import time, socket, ssl
host, port = "example.com", 443
t0 = time.time()
sock = socket.create_connection((host, port), timeout=5)
t_tcp = time.time() - t0
ctx = ssl.create_default_context()
tls = ctx.wrap_socket(sock, server_hostname=host)
t_tls = time.time() - t0
tls.sendall(b"HEAD / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")
resp = tls.recv(1024)
print(f"tcp={t_tcp:.3f}s tls={t_tls:.3f}s status={resp.split()[1].decode()}")这段代码手动完成 TCP 连接、TLS 握手、发一个 HTTP 请求,把各阶段耗时打出来。真实项目里更推荐用成熟客户端库,但这种“手搓一次”的经历,能让你真正理解协议栈的分层。
第一是分层思维。知道一个问题属于 DNS、TCP、TLS、HTTP 还是应用层,才能选对工具。分层清晰,排查就不会乱。
第二是可观测性。浏览器 DevTools、curl -w、抓包工具(Wireshark、tcpdump)、服务端日志,要能串起来看。没有时间戳对齐,跨层排查很难。
第三是版本差异。HTTP/1.1、HTTP/2、HTTP/3 的行为差异很大,缓存、连接复用、头部处理都不同。不能拿旧经验套新协议。
第四是安全与性能的平衡。TLS 配置、HSTS、CSP、CORS,既要安全又要可用,配置错误会直接导致功能不可用。
第五是中间设备的影响。代理、网关、CDN、负载均衡都会改写请求响应。协议问题经常不在两端,而在中间。
Web 协议的价值,不在于背下每个头部字段,而在于建立一张“请求生命周期”的地图。知道一次请求经过了哪些层、每层可能出什么问题、用什么工具观察,遇到“偶发超时”“证书报错”“跨域失败”“首屏慢”时,就不会只能重启试试。对开发者来说,最务实的做法是:先用 curl -w 和浏览器 DevTools 把常见阶段耗时看清楚,再逐步深入 TLS、HTTP/2、QUIC 的细节。协议是地基,平时看不见,出问题时才知道它有多重要。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。