Appearance
第3章:计算机网络——学会定位“为什么请求失败”
3.1 一次 HTTP 请求到底经历了什么?
当用户在浏览器中访问一个 HTTPS 网站时,完整的底层物理与协议流转如下:
mermaid
sequenceDiagram
autonumber
participant Browser as 客户端浏览器
participant DNS as DNS 递归解析器
participant Gateway as Nginx 反向代理/网关
participant App as 后端 Web 服务
participant DB as 数据库/缓存
Browser->>DNS: 1. 域名解析查询 (A/AAAA 记录)
DNS-->>Browser: 返回目标 IP 地址
Browser->>Gateway: 2. TCP 三次握手 + TLS 安全握手协商
Browser->>Gateway: 3. 发送 HTTP 请求报文
Gateway->>App: 4. 路由反向代理转发 (请求头注入 X-Forwarded-For)
App->>DB: 5. 执行业务逻辑、查询缓存或 SQL 读写
DB-->>App: 返回数据结果
App-->>Gateway: 6. 业务服务响应输出
Gateway-->>Browser: 7. 响应报文回传并渲染排查网络故障时,你至少要区分这六个层级:
- DNS 层:
- 域名解析是否正确?操作系统 DNS 缓存、本地 hosts、TTL 过期或权威 DNS 递归异常,往往会导致**“有的人能访问、有的人死活打不开”**。
- TCP 网络连接层:
- 连接超时(Connection Timeout)、连接拒绝(Connection Refused,目标端口未监听)、连接重置(Connection Reset by Peer,防火墙拦截或进程崩溃),这三者绝不是同一种故障!
- TLS 安全加密层:
- SSL 证书过期、证书链不完整、主机名不匹配(CN/SAN 错误)以及 TLS 协议版本套件协商不一致均会导致握手在传输初期瞬间中断。
- HTTP 应用语义层:
- HTTP 状态码具有严格的语义导向:
401 Unauthorized:未登录 / Token 缺失或过期;403 Forbidden:已登录但权限不足;404 Not Found:URL 路由错误或资源不存在;429 Too Many Requests:触发接口速率限制;500 Internal Server Error:业务应用代码未捕获异常。
- HTTP 状态码具有严格的语义导向:
- 代理与网关层(Reverse Proxy):
- Nginx 的超时配置(
proxy_read_timeout)、请求体上限(client_max_body_size)、转发头丢失或代理层缓存均可能改变系统响应。
- Nginx 的超时配置(
- 业务服务与数据库层:
502 Bad Gateway与504 Gateway Timeout很多时候只是表象,其根本原因往往是后端微服务内存耗尽死掉、或数据库产生了全表扫描慢查询拖垮了连接池。
WARNING
重要核心原则:线上遇到接口变慢或失败时,千万不要草率地把所有“超时”都归结成“用户网络不好”! 必须首先根据全链路耗时分段(DNS 耗时、连接建立耗时、TTFB 首字节耗时、数据传输耗时)定位真正瓶颈。
3.2 GET、POST 和幂等性设计
幂等的本质
HTTP 方法的规范语义至关重要。幂等(Idempotence)的核心定义是:“同一个操作执行一次和连续执行多次,其对系统状态产生的预期影响完全一致”;它绝不是指“该请求只会被发送一次”。
在不可靠的网络环境中,客户端超时可能会自动重试,而此时服务端可能早已经处理成功,仅仅是回传的 HTTP 响应在网络链路中意外丢失。
mermaid
graph TD
User["用户连续点击两次提交订单"] --> Client["前端 App"]
Client -->|"请求1: 带幂等键 req_key_001"| Server["后端服务"]
Server -->|"在事务中验证幂等键并创建订单"| DB[("数据库")]
Server -.->|"响应网络超时丢包"| Client
Client -->|"请求2: 自动重试重发 req_key_001"| Server
Server -->|"检查发现 req_key_001 已存在"| DB
Server -->> Client["直接返回首次订单创建成功的结果, 严禁重复扣费生成新单!"]以“创建会员订单”为例的工程化防重流程:
- 防重机制前置:如果前端用户因卡顿连续快速点击两次,后端必须绝对保障同一个业务请求不会生成两份订单或扣减两次余额;
- 幂等键(Idempotency Key)设计:要求客户端在请求头中携带唯一生成的业务幂等键(如 UUID),后端将该键与订单结果持久化到存储中;
- 依赖数据库唯一约束:
- 严禁仅在应用服务器内存缓存中做防重(无法抵御微服务多节点并发与进程重启);
- 严禁采用“先
SELECT查询是否存在,若不存在再INSERT”的脆弱逻辑(高并发下必发生并发穿透); - 必须在数据库层面设置唯一索引约束(Unique Index)或基于数据库原子事务完成插入与锁定!
3.3 必做实验与验收自测
必做实验:
使用 curl -v 或 Chrome 开发者工具观察一个真实的 HTTPS 请求,详细记录并分析:
- DNS 解析时间;
- TCP 连接与 TLS 握手时间;
- TTFB(Time to First Byte,等待服务端响应时间);
- Content Download(数据包内容下载耗时)。
故障模拟演练: 在本地人为构造并模拟以下四种错误,观察 curl 输出差异并解释其根本区别:
- 错误的不存在的域名(验证 DNS 报错);
- 目标 IP 正确但端口未监听(验证 Connection Refused);
- 后端应用抛出未捕获异常(验证 HTTP 500);
- Nginx 代理等待后端处理超时(验证 HTTP 504 Gateway Timeout)。
验收思考题(检验是否真正掌握):
- 为什么客户端收到“连接超时(Timeout)”并不表示服务端一定没有执行成功?
- 为什么在设计重试支付、创建订单或对外发送邮件等非幂等接口时,必须在服务端强制设计去重防重机制?