支撐十萬級甚至百萬級並發 TCP 連接,單靠業務代碼優化遠不夠,必須從作業系統層面系統性打通「文件描述符上限 → 本地端口範圍 → 連接追蹤表 → TCP 協議棧參數 → 網路 I/O 模型」五大瓶頸。本文匯總生產環境驗證的完整調優清單,涵蓋 ulimit/limits.conf/pam_limits、ip_local_port_range/ip_conntrack_max、sysctl.conf 核心參數模板、epoll/io_uring 程式模型選擇,並附帶驗證腳本與常見坑點。
ulimit -n = 1024,扣除標準輸入/輸出/錯誤、監聽 socket、unix domain socket 等,實際可用 ~1010。/etc/security/limits.conf):* soft nofile 102400
* hard nofile 102400
root soft nofile 102400
root hard nofile 102400
/etc/pam.d/login、/etc/pam.d/sshd、/etc/pam.d/system-auth):session required pam_limits.so
/proc/sys/fs/file-max、/etc/sysctl.conf):fs.file-max = 2000000
fs.nr_open = 2000000 # 單進程理論上限 (內核 2.6.25+)
生效驗證:重新登入 Shell 或重啟服務,ulimit -n 應顯示 102400。systemd 服務 需在 Unit 檔添加 LimitNOFILE=102400。
客戶端發起連接需佔用一個本地臨時端口(預設 32768-60999,約 2.8 萬個)。單 IP 高併發短連接場景極易耗盡,報錯 EADDRNOTAVAIL: Cannot assign requested address。
# /etc/sysctl.conf
net.ipv4.ip_local_port_range = 1024 65535
# 理論可用 ~6.4 萬端口,配合 SO_REUSEPORT 可更高
生效:sysctl -p。注意:最小值 ≥ 1024,最大值 ≤ 65535。
Netfilter 連接追蹤表預設 65536 條目,高併發時滿表導致新連接 SYN_RECV 掛起、最終超時,現象為 connect() 卡住無報錯。dmesg 顯示 nf_conntrack: table full, dropping packet。
# /etc/sysctl.conf
net.netfilter.nf_conntrack_max = 2000000
net.netfilter.nf_conntrack_tcp_timeout_established = 600
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 30
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 30
# 內核 4.7+ 建議同時設置
net.nf_conntrack_max = 2000000
記憶體估算:每條目 ~300-400 Bytes,200 萬條 ≈ 600-800 MB,確保主機記憶體充足。
# /etc/sysctl.conf —— 高併發生產級模板(參考 Varnish/SunOne/Cloudflare 最佳實踐)
# 連接隊列與建立
net.core.somaxconn = 262144 # listen() backlog 上限,配合應用 listen(fd, 262144)
net.ipv4.tcp_max_syn_backlog = 262144 # SYN 接收隊列
net.ipv4.tcp_syncookies = 1 # 防 SYN Flood
net.ipv4.tcp_synack_retries = 2 # SYN+ACK 重試次數
net.ipv4.tcp_syn_retries = 2 # SYN 重試次數
net.ipv4.tcp_max_orphans = 262144 # 孤兒連接上限
# 快速回收與復用
net.ipv4.tcp_tw_reuse = 1 # 開啟 TIME_WAIT 復用 (客戶端/負載均衡端極其重要)
net.ipv4.tcp_tw_recycle = 0 # 禁用快速回收 (NAT 環境下會導致連接失敗,內核 4.12+ 已移除)
net.ipv4.tcp_fin_timeout = 10 # FIN_WAIT2 超時縮短 (預設 60s)
# 緩衝區優化(動態調整,單位:bytes)
net.core.rmem_max = 16777216 # 接收緩衝區最大
net.core.wmem_max = 16777216 # 發送緩衝區最大
net.ipv4.tcp_rmem = 4096 87380 16777216 # min, default, max
net.ipv4.tcp_wmem = 4096 65536 16777216
# 窗口擴展與選項
net.ipv4.tcp_window_scaling = 1 # 啟用窗口縮放 (RFC 1323)
net.ipv4.tcp_sack = 1 # 選擇性確認
net.ipv4.tcp_timestamps = 1 # 時間戳 (配合 PAWS 防序列號繞回)
net.ipv4.tcp_no_metrics_save = 1 # 不緩存連接指標,避免舊指標誤判新連接
# 網路設備佇列
net.core.netdev_max_backlog = 30000 # 封包處理佇列
# 其它
net.ipv4.tcp_slow_start_after_idle = 0 # 閒置後不重置慢啟動
net.ipv4.tcp_mtu_probing = 1 # 啟用 MTU 探測
net.ipv4.tcp_abort_on_overflow = 0 # 接收佇列滿時發 RST (0=丟棄, 1=RST)
生效:sysctl -p + sysctl -w net.ipv4.route.flush=1 或重啟網路服務。
| 模型 | 適用場景 | 優勢 | 劣勢 | 代表框架 |
|---|---|---|---|---|
| select | ≤ 1024 連接 | 跨平台、簡單 | FD_SETSIZE 限制、O(n) 掃描、複製開銷 | 早期框架 |
| poll | ≤ 數萬連接 | 無 FD 上限 | O(n) 掃描、複製開銷、公平性差 | 部分舊框架 |
| epoll (LT/ET) | 十萬-百萬連接 | O(1) 就緒通知、邊緣觸發 (ET) 低延遲、內核 2.6+ 原生 | 僅 Linux、ET 模式編程複雜 | Nginx、Redis、Netty、libevent、libuv、Go netpoller |
| io_uring | 百萬+ 連接、高 IOPS | 真異步、零拷貝、批量提交/完成、內核 5.1+ | 較新、庫支援尚在成熟、需內核 5.10+ 穩定 | Seastar、tokio-uring、liburing、nginx 1.25+ 實驗支援 |
| AIO (libaio) | 檔案非阻塞 IO | 磁碟異步 IO | 網路 IO 支援有限、早期實現為內核執行緒池 | 資料庫、存儲引擎 |
| 坑點 | 現象 | 根因 | 規避 |
|---|---|---|---|
| limits.conf 不生效 | ulimit -n 仍為 1024 | 缺 pam_limits.so / systemd 服務未設 LimitNOFILE / 登入非 login shell | 檢查 /etc/pam.d/*、systemd Unit、/etc/profile 無覆蓋 ulimit |
| tcp_tw_recycle=1 導致 NAT 斷連 | 同公網 IP 多客戶端連接失敗 | 時間戳檢查失敗,內核 4.12+ 已移除此參數 | 堅決設 0,改用 tcp_tw_reuse=1 |
| conntrack 表滿但無日誌 | 連接卡住、無錯誤碼 | 內核預設不打印,需開啟 net.netfilter.nf_conntrack_log_invalid=1 | 監控 nf_conntrack_count / nf_conntrack_max 比例 > 80% 告警 |
| somaxconn 大但應用 listen backlog 小 | 連接排隊溢出 | 應用碼寫死 listen(fd, 128) | 框架配置項調大,或代碼讀取環境變數 |
| epoll ET 模式漏事件 | 連接卡死、數據不全 | 未循環讀寫至 EAGAIN / 未處理 EPOLLHUP/EPOLLERR | 嚴格遵循 ET 讀寫循環模式 |
limits.conf + pam_limits + fs.file-max + systemd LimitNOFILE → 100k+。ip_local_port_range = 1024 65535 → 6.4萬+。nf_conntrack_max = 2M + 縮短超時 → 避免表滿。sysctl.conf 模板 → 隊列、緩衝、快速回收、窗口擴展。將上述清單納入基線映像、CI/CD 部署前檢查、壓測驗收指標,讓高併發能力成為可預期、可度量、可復製的工程成果,而非臨時抱佛腳的運氣遊戲。