--- tags: [计算机网络, TCP调优, sysctl, BBR, TIME_WAIT, KeepAlive, 连接复用] create time: 2026-05-18 05:15 --- # TCP 内核调优与连接复用 ## 概述 网络性能优化的核心公式是:**用户体验 = ∑(RTT_i × 系数_i) + 数据量 / 实际吞吐量**。本章从操作系统内核参数入手,到传输层之上的 HTTP/gRPC keep-alive,逐层展开优化手段。 ## TCP 内核参数全面调优 ### 参数速查表(按优先级排序) ```bash # ⭐ 第一优先级 — 这些不改,高并发必瓶颈 $ sysctl net.core.somaxconn # socket 监听 backlog 上限 (默认 128 → 改为 65535!) $ sysctl net.ipv4.tcp_max_syn_backlog # SYN 半连接队列大小 (默认 ~1024 → 改为 8192) $ sysctl net.ipv4.tcp_congestion_control # 拥塞控制算法 (默认 cubic → bbr) # ⭐ 第二优先级 — 缓冲区大小决定高带宽延迟积场景的吞吐量 $ sysctl net.core.rmem_max # max recv buffer (默认 212992 → 改为 16MB) $ sysctl net.core.wmem_max # max send buffer (默认 212992 → 改为 16MB) $ sysctl net.ipv4.tcp_rmem # auto/min/max recv buffer (默认 4096 87380 6291456) $ sysctl net.ipv4.tcp_wmem # auto/min/max send buffer # ⭐ 第三优先级 — TIME_WAIT 管理 $ sysctl net.ipv4.tcp_tw_reuse # 复用 TIME_WAIT 套接字做 outbound (默认 0 → 改为 1) $ ⚠️ tcp_tw_recycle 已从 Linux 4.12 永久移除!不要尝试启用 ``` ```bash # 第四优先级 — 高级功能 $ sysctl net.ipv4.tcp_fastopen # TCP Fast Open (0=off, 3=enable both) $ sysctl net.ipv4.tcp_timestamps # 时间戳选项 (SACK/BBR 需要, 默认 1) $ sysctl net.ipv4.tcp_sack # Selective ACK (默认 1, 别关) $ sysctl net.ipv4.tcp_fack # Forward ACK (配合 SACK) $ sysctl net.ipv4.tcp_limit_output_bytes # TCP 输出队列限制 ``` ```bash # 第五优先级 — Keepalive 调优 $ sysctl net.ipv4.tcp_keepalive_time # 首次探测前的空闲时间 (默认 7200s → 改为 600s) $ sysctl net.ipv4.tcp_keepalive_intvl # 两次探测间隔 (默认 75s → 改为 30s) $ sysctl net.ipv4.tcp_keepalive_probes # 最多探测次数 (默认 9 → 改为 3) # 总断开时间 = tcp_keepalive_time + (tcp_keepalive_intvl × tcp_keepalive_probes) # 原来: 7200 + (75 × 9) = 7875s ≈ 2.2h # 新值: 600 + (30 × 3) = 690s ≈ 11.5min # 端口范围(连接池场景下扩大临时端口) $ sysctl net.ipv4.ip_local_port_range # 默认 32768-60999 → 改为 1024-65535 ``` ### /etc/sysctl.conf 生产级持久化配置 ```ini # ========================================== # Web/API 服务器 TCP 调优配置 # ========================================== # === 连接队列 === net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 8192 net.ipv4.tcp_abort_on_overflow = 0 # === 缓冲区 (BDP 适配: Buffer = Bandwidth × RTT) === net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.core.rmem_default = 16777216 net.core.wmem_default = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 # === 拥塞控制算法 === net.ipv4.tcp_congestion_control = bbr net.ipv4.tcp_mux = 1 # BBR 推荐开启 multiplex # === TIME_WAIT === net.ipv4.tcp_tw_reuse = 1 # === Keepalive === net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_intvl = 30 net.ipv4.tcp_keepalive_probes = 3 # === 端口范围 === net.ipv4.ip_local_port_range = 1024 65535 # === 高级特性 === net.ipv4.tcp_fastopen = 3 net.ipv4.tcp_timestamps = 1 net.ipv4.tcp_sack = 1 ``` ```bash # 生效配置 $ sudo sysctl -p $ # 验证拥塞控制算法 $ cat /proc/net/sockstat $ ss -i # 查看当前连接的拥塞控制算法 ``` ## Keep-Alive 与连接复用全栈方案 ### 为什么 connection reuse 这么重要? ``` 一次全新 TCP 连接的开销: ───────────────────────────── TCP 三次握手 = 1 RTT (TCP Fast Open 可 0-RTT) TLS 握手 TLS1.3 = 1 RTT (Session Resumption 可 0-RTT) HTTP Request = 1 RTT ───────────────────────────── 总计 = 2~3 RTT 如果复用连接: ───────────────────────────── 已有连接上新请求 = 0 RTT (零额外等待) ─────────────────────────────────── 每次请求节省 2~3 RTT! 对于跨洋链路 (RTT ≈ 150ms), 每次请求省 450ms! ``` ### HTTP Keep-Alive —— Go 客户端侧 ```go // Go http.Transport 的连接池配置 transport := &http.Transport{ MaxIdleConns: 100, // 全局最大空闲连接数 MaxIdleConnsPerHost: 10, // 每个 host 最大空闲连接数 IdleConnTimeout: 90 * time.Second, // 空闲超此时间关闭 // TLS 优化: Session Ticket 实现 TLS 1.3 0-RTT resumption TLSClientConfig: &tls.Config{ SessionTicketsDisabled: false, // 默认开启 }, // 禁用压缩 header(避免 CRIME/BREACH 攻击) DisableCompression: true, } client := &http.Client{Transport: transport} // 后续的 client.Get() 自动复用已建立的 TCP + TLS 连接 ``` ### HTTP Keep-Alive —— Nginx 反向代理侧 ```nginx upstream backend { server app1:8080; server app2:8080; keepalive 32; # 保持 32 个空闲连接到后端 keepalive_requests 10000; # 单个连接最多服务 10000 个请求 keepalive_timeout 60s; # 空闲超时 } server { location /api { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; # 清空 Connection: close # 关键: Nginx 默认使用 HTTP/1.0 向后端转发,不带 keepalive # 必须显式设置 proxy_http_version 1.1 + 清空 Connection header } } ``` ### gRPC KeepAlive —— 双向心跳 ```go import "google.golang.org/grpc/keepalive" grpc.NewServer( // 服务端参数 grpc.KeepaliveParams(keepalive.ServerParameters{ Time: 10 * time.Second, // 每 10s 发 ping Timeout: 5 * time.Second, // 等 5s 回复 pong MaximumConnectionIdle: 5 * time.Minute, // 最大空闲连接存活时间 }), // 客户端策略强制执行 grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ MinTime: 5 * time.Second, // 客户端至少每 5s 发一次 ping PermitWithoutStream: true, // 即使没有活跃流也允许 ping }), ) ``` > [!question] gRPC 为什么需要独立于 HTTP/2 的 KeepAlive? > HTTP/2 本身有 PING frame,但 gRPC 在应用层额外加了一层——这是因为中间可能有 L7 load balancer(如 Envoy),它会主动关闭长连接。gRPC 的 KeepAlive 可以让负载均衡器认为连接仍然活跃。 ## 关联笔记 - [[hhs/NETWORK/TCP流量控制与拥塞控制]] — BBR vs Cubic 算法原理 - [[hhs/NETWORK/TCP段结构与状态机]] — TIME_WAIT / CLOSE_WAIT 分析 - [[hhs/NETWORK/CDN与负载均衡]] — CDN 缓存策略与 Load Balancer 选型 - [[hhs/NETWORK/GoServer终极调优]] — Go http.Server 超时配置的深入讲解