7.1 KiB
7.1 KiB
tags, create time
| tags | create time | |||||||
|---|---|---|---|---|---|---|---|---|
|
2026-05-18 05:15 |
TCP 内核调优与连接复用
概述
网络性能优化的核心公式是:用户体验 = ∑(RTT_i × 系数_i) + 数据量 / 实际吞吐量。本章从操作系统内核参数入手,到传输层之上的 HTTP/gRPC keep-alive,逐层展开优化手段。
TCP 内核参数全面调优
参数速查表(按优先级排序)
# ⭐ 第一优先级 — 这些不改,高并发必瓶颈
$ 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 永久移除!不要尝试启用
# 第四优先级 — 高级功能
$ 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 输出队列限制
# 第五优先级 — 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 生产级持久化配置
# ==========================================
# 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
# 生效配置
$ 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 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 反向代理侧
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 —— 双向心跳
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 超时配置的深入讲解