为什么打开 TCP 套接字开销大:握手、状态与系统成本
副标题 / 摘要 TCP 连接不是一次函数调用,而是一组协议状态机与内核资源分配。本文解释开销来源,并给出工程上的优化路径。 目标读者 负责网络服务优化的后端工程师 想理解连接成本的系统开发者 需要排查连接耗时的运维与 SRE 背景 / 动机 在高并发系统里,“频繁建连”常常成为性能瓶颈。 理解 TCP 连接开销来源,才能知道何时该用连接池、何时该复用、何时该改协议。 核心概念 三次握手:SYN/SYN-ACK/ACK 内核状态:连接表、套接字缓冲区、TCP 状态机 慢启动:初始窗口小、吞吐从低到高 系统调用成本:socket/connect/accept 带来上下文切换 实践指南 / 步骤 优先复用连接(HTTP keep-alive / 连接池) 减少短连接,批量或长连接替代 降低握手成本(TLS session resumption) 调优内核参数(连接队列、端口范围) 监控连接层指标(SYN 重传、TIME_WAIT) 可运行示例 下面脚本在本机测量多次建连成本: import socket import threading import time def server(port_holder, ready, n): s = socket.socket() s.bind(("127.0.0.1", 0)) port_holder.append(s.getsockname()[1]) s.listen() ready.set() for _ in range(n): conn, _ = s.accept() conn.close() s.close() def measure(n=200): port_holder = [] ready = threading.Event() t = threading.Thread(target=server, args=(port_holder, ready, n), daemon=True) t.start() ready.wait() port = port_holder[0] start = time.perf_counter() for _ in range(n): c = socket.create_connection(("127.0.0.1", port)) c.close() elapsed = time.perf_counter() - start print(f"{n} connections: {elapsed:.3f}s") if __name__ == "__main__": measure() 解释与原理 TCP 连接要维护状态机、缓冲区、窗口、重传计时器。 每次建连都需要三次握手与内核资源分配,还会触发慢启动,吞吐无法立即拉满。 ...