Files
cs-note/hhs/NETWORK/09-性能优化/01-TCP内核调优与连接复用.md
T
2026-05-24 11:42:38 +08:00

199 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 超时配置的深入讲解