一次请求的完整旅程:DNS、TLS、代理与应用

用户看到“页面转圈”,原因可能在网络,也可能是数据库连接池排队。先把请求经过哪些地方弄清,排错才不会只盯着业务函数。

1. 后端程序在等什么

服务端进程监听某个地址和端口。客户端找到目标地址后建立连接,再按协议交换数据。域名解析负责把名称映射到地址等信息;TLS 负责通信加密与身份验证;HTTP 规定请求和响应语义。这些机制不等于业务权限检查:连接经过 HTTPS,并不说明这个用户有权查询别人订单。

HTTP/1.1 和 HTTP/2 通常运行在 TCP 上;HTTP/3 使用基于 UDP 的 QUIC。复用连接、DNS 缓存、TLS 会话恢复会改变实际路径,因此并非每个请求都重新经历完整建连过程。

2. 反向代理和负载均衡

反向代理代表服务端接收请求,再转交应用。负载均衡是在多个后端之间选择目标;它可以由代理完成,也可能发生在别的网络层。应用有多个实例后,进程内变量只属于当前实例。把登录态或订单状态只放在本机内存,换一个实例就可能读不到。

flowchart LR U[客户端] --> P[代理或负载均衡] P --> A[应用实例 A] P --> B[应用实例 B] A --> D[数据库] B --> D

图里的数据库可以有复制和故障转移,但这些是另一个层面的设计。两台应用连同一个数据库,不代表数据库已经具备高可用。

代理还可能改写路径、终止 TLS、限制请求大小。应用只有在请求确实来自可信代理时,才应采信它传递的客户端地址或协议头。否则用户也可以自己发送同名 Header。

3. 应用收到请求以后

典型顺序是请求大小与格式检查、认证、授权、业务校验、事务处理和响应编码。不同框架的中间件安排不完全相同。业务校验应和权限一起发生在服务端,前端校验主要是帮助用户更早发现输入问题。

每一层都可能排队:连接建立、代理队列、应用执行槽位、数据库连接池、行锁。总延迟可拆成等待时间和真正处理时间。CPU 很闲但请求很慢,可能是在等数据库,而不是需要再加 CPU。

下面是本机教学服务器,只演示“接请求—按路径返回响应”,不实现用户系统或持久化。Python 3.11+ 保存为 server.py 后运行;只监听回环地址。

Python
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer import json class Handler(BaseHTTPRequestHandler): def do_GET(self): if self.path != "/health": self.send_error(404) return body = json.dumps({"process": "alive"}).encode("utf-8") self.send_response(200) self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) if __name__ == "__main__": ThreadingHTTPServer(("127.0.0.1", 8080), Handler).serve_forever()

另开终端执行 curl -i http://127.0.0.1:8080/health。这个 health 只证明进程在响应,未证明数据库可用。生产服务还要考虑负载、认证、超时、优雅退出和可信代理配置,标准库示例不承担这些保障。

4. 怎样定位慢在哪里

为一次请求分配 request ID,记录入口时间、业务结束时间和数据库耗时。再分别测试代理地址与应用内部地址,判断慢点是否发生在转发之前。不要通过关闭 TLS 或绕过权限来“优化”结果。

用几个受控实验建立直觉:业务函数睡眠一秒;把连接池限制为一个;让两个事务竞争同一行。比较 CPU、排队长度和延迟的变化。三种慢法表现不同,处理方式也不同。

自检与参考

为什么两个应用实例并不自动提供共享登录态?为什么 CPU 使用率低仍会超时?试着沿上图解释,而不是只说“服务器卡了”。