上一篇 下一篇 分享链接 返回 返回顶部

我在香港机房为电竞平台“止掉线”的那一夜:Windows Server 2019 Socket 与内核参数优化全记录

发布人:Minchunlin 发布时间:2025-09-22 10:27 阅读量:823


晚上 7 点半,香港机房值班电话响了。华南高峰期刚起,平台观战房与匹配服掉线报警从 0.6% 蹿到 2.8%,客服群里玩家在刷“频繁重连”。我盯着 NOC 大屏:丢包抖动主要出现在回源(CN↔HK)段,但服务端重传与半开连接也异乎寻常。

我决定先稳住:开只读扩容、分流静态资源,随后直接进机房对几台关键的 Windows Server 2019 做“低时延取向”的内核与 Socket 优化。

环境与基线(上手前先摸清家底)

硬件/系统与网卡

  • 机型:Supermicro 1U(类似 SYS-1029 系列),
  • CPU:Intel Xeon Silver 4214R ×2(24C/48T,总主频 2.4GHz,睿频 3.5GHz)
  • 内存:128 GB DDR4-2666
  • 系统盘:双盘 NVMe(RAID1)
  • 网卡:Intel X710-DA2(10GbE,单链路上行)
  • OS:Windows Server 2019 Datacenter(1809 系列内核,打到近半年补丁)
  • 业务:.NET Core 网关 + 长连接会话层(WebSocket/TCP 443),匹配服务(TCP 8443),房间心跳(UDP 20000-20100,少量)

基线指标(变更前 10 分钟滚动平均)

指标 数值
掉线率(玩家侧 1 分钟内需重连的会话占比) 2.8%
TCP 重传率(服务端视角) 2.1%
95% RTT(CN↔HK) ~320 ms
SYN 超时比(3 次握手未成比例) 0.9%
TIME_WAIT 总量 > 120k
WebSocket 断连后 3 秒内重连成功率 88%

快照检查(保留状态,支持回滚)

# 全局 TCP 设置
netsh int tcp show global

# TCP 模板(Windows 2019 有“Internet”等模板)
Get-NetTCPSetting | ft SettingName,AutoTuningLevelLocal,ECNCapability,CongestionProvider,Timestamps

# 网卡队列与卸载能力
Get-NetAdapter | ft Name,Status,LinkSpeed
Get-NetAdapterRss | ft Name,Enabled,BaseProcessorNumber,MaxProcessors,Profile
Get-NetAdapterAdvancedProperty -Name "Ethernet 10G" | ft DisplayName,DisplayValue

变更策略与风险控制

  • “先测后改,小步快跑”:按模块推进,每一步仅改 2~3 项参数,并记录指标。
  • “能回滚就回滚”:每组命令都有反向操作;关键注册表变更先导出。
  • “针对链路特性”:CN↔HK 长肥管道 + 偶发拥塞,目标是提升拥塞信号敏感度、加快死链检测、减少 TIME_WAIT 压力,同时把 CPU/网卡中断压在“可控低延迟区间”。

Step 1:电源与调度,把“系统级抖动”按住

切到高性能电源计划(减少 C-State 进出延迟)

# 开启“终极性能”并切换(Server 2019 可用)
powercfg -duplicatescheme e9a42b02-d5df-448d-aa00-03f14749eb61
powercfg /S e9a42b02-d5df-448d-aa00-03f14749eb61

# 可选:把最小处理器状态拉到 100%
powercfg -setacvalueindex scheme_current sub_processor PROCTHROTTLEMIN 100
powercfg -setactive scheme_current

效果:CPU 睿频/降频抖动显著减少,线程调度稳定。

Step 2:网卡与队列(RSS/RSC/LSO),决定“每个包落哪颗核”

目标:让高并发短报文分摊到多个核心,避免单核中断打满;同时按“低时延优先”策略谨慎使用合并/分段卸载。

开启 RSS,固定亲和,贴近 NUMA

# 开启 RSS 并设置亲和
Enable-NetAdapterRss -Name "Ethernet 10G"
Set-NetAdapterRss -Name "Ethernet 10G" -BaseProcessorNumber 2 -MaxProcessors 8 -Profile Closest

RSC/LSO 策略

  • RSC(接收段合并):吞吐友好,但可能增加尾延迟。我们的实时业务以低延迟为先,禁用。
  • LSO(大包分段卸载):CPU 省事,但某些驱动版本在高 PPS 下引入 jitter。我们 保留 LSO,若尾延迟不降再禁用对比。
# 禁用 RSC
Set-NetAdapterRsc -Name "Ethernet 10G" -IPv4 Disabled -IPv6 Disabled

# 保留 LSO(若要禁用以对比)
# Disable-NetAdapterLso -Name "Ethernet 10G"

小结(阶段性观测)

  • 单核中断峰值下降约 35%,队列更均衡。
  • 95% RTT 有轻微改善(~300 ms)。

Step 3:TCP 栈“低时延取向”——拥塞控制、ECN、时间戳、窗口自调

目标:对拥塞更敏感(早知道早收敛),提升可观测性(时间戳),保证跨境链路的收发窗口自适应。

选择拥塞算法 + 打开 ECN + 开启时间戳 + 保持自适应窗口

# 以“Internet”模板为基础,直接调模板
Set-NetTCPSetting -SettingName Internet `
  -CongestionProvider CUBIC `
  -ECNCapability Enabled `
  -Timestamps Enabled `
  -AutoTuningLevelLocal Normal

说明:

  • CUBIC:对高 BDP 链路更友好,收敛/恢复速度适配跨境场景。
  • ECN:让队列溢出前通过标记反馈拥塞,减少硬丢包与重传。
  • Timestamps:支持 PAWS/RTT 估计,更稳定。
  • AutoTuning Normal:大多数中间盒可兼容;若遇到“窗口黑洞”,降到 Restricted。

验证

netsh int tcp show global
Get-NetTCPSetting -SettingName Internet | fl

Step 4:端口与 TIME_WAIT 压力管理(大规模短连接场景关键)

扩大动态端口区间(避免端口耗尽)

# TCP/UDP 动态端口范围
netsh int ipv4 set dynamicport tcp start=10000 num=55535
netsh int ipv4 set dynamicport udp start=10000 num=55535

降低 TIME_WAIT 保留时间(默认 120s → 30s)

谨慎:降太低会增加端口复用碰撞风险;30s 在我们业务里验证可行。

# 先备份注册表
reg export "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" C:\reg_tcpip_bak.reg

# 设置 TcpTimedWaitDelay = 30(单位:秒)
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" `
 -Name "TcpTimedWaitDelay" -PropertyType DWord -Value 30 -Force

阶段效果

  • TIME_WAIT 数量峰值从 120k 降至 ~45k。
  • 某些反向代理进程端口回收更顺畅,SYN 拒绝明显下降。

Step 5:Keep-Alive 与死链探测(WebSocket 业务必须快准狠)

Windows 默认 Keep-Alive 2 小时太慢,长链业务要主动探测,服务端与客户端一致调快。

全局 Keep-Alive(系统级兜底)

# KeepAliveTime = 15000 ms(15s),KeepAliveInterval = 1000 ms(1s)
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" `
 -Name "KeepAliveTime" -PropertyType DWord -Value 15000 -Force
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" `
 -Name "KeepAliveInterval" -PropertyType DWord -Value 1000 -Force

应用层 Socket 选项(.NET Core 侧)

// 关键摘录:为长连接/实时推送的 Socket 明确低延迟取向
var socket = tcpClient.Client;
socket.NoDelay = true; // TCP_NODELAY,禁用 Nagle
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

// Windows 下用 IOControl 精细化 KA 定时
// 结构:onoff, keepalivetime(ms), keepaliveinterval(ms)
var ka = new byte[12];
BitConverter.GetBytes(1).CopyTo(ka, 0);         // on
BitConverter.GetBytes(15_000).CopyTo(ka, 4);    // 15s
BitConverter.GetBytes(1_000).CopyTo(ka, 8);     // 1s
socket.IOControl(IOControlCode.KeepAliveValues, ka, null);

// 连接关闭策略:避免长时间 FIN_WAIT
socket.LingerState = new LingerOption(false, 0); // 立即 RST 关闭(按需使用)

注:RST 关闭会让对端立刻感知断链,减少半闭等待;但会丢未发送完的数据,请确保上层有幂等/重试。

Step 6:SYN/重传参数(更稳握手,更快失败)

# 降低 SYN 重试次数(默认 6),我们设为 4,避免“假死等太久”
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" `
 -Name "TcpMaxSynRetransmissions" -PropertyType DWord -Value 4 -Force

# 数据重传上限(默认 5),保持 5 或 6,视链路而定
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" `
 -Name "TcpMaxDataRetransmissions" -PropertyType DWord -Value 5 -Force

Step 7:MTU/PMTUD 与分片(跨境“黑洞”要小心)

默认 1500 MTU。跨境链路若遇到隧道/PPPoE,可能有效 MTU < 1500。

策略:保持 PMTUD 开启,若观测到 ICMP 受限导致“黑洞”,再在边界设备做 MSS Clamping。

# 显示并确保 pmtudiscovery 启用(一般默认启用)
netsh interface ipv4 show global
# 需要时固定某接口 MTU(示例)
# netsh interface ipv4 set subinterface "Ethernet 10G" mtu=1500 store=persistent

Step 8:内核诊断与压测回路(别拍脑袋,靠证据)

快速采样

# 网络连接失败与重传
Get-Counter "\TCPv4\Connection Failures","\\TCPv4\Segments Retransmitted/sec" -SampleInterval 5 -MaxSamples 12

# PktMon 抓包(Server 2019 可用)
pktmon filter add -d ipv4
pktmon start --etw -p 0

# 结束并导出
pktmon stop
pktmon etl2pcapng PktMon.etl -o PktMon.pcapng

端到端连通性/端口耗尽检测

Test-NetConnection -ComputerName gw.example.hk -Port 443
netstat -ano | findstr /c:"TIME_WAIT" | measure

吞吐/延迟对照(内网链路)

生产不建议长时间跑;用维护窗口做样本。

iperf3(Windows 版)可用来确认 RSS/RSC/LSO 对吞吐与尾延迟的影响。

阶段性结果(改完一轮后的 20 分钟)

指标 变更前 变更后(20 分钟)
掉线率 2.8% 0.9%
TCP 重传率 2.1% 0.8%
95% RTT ~320 ms ~210 ms
SYN 超时比 0.9% 0.3%
TIME_WAIT 总量 >120k ~45k
3 秒内重连成功率 88% 97%

我让值班同事继续观察 1 小时,再决定是否做第二轮(例如禁用 LSO 进一步榨延迟、或把 AutoTuning 调到 Restricted 以防某些链路黑洞)。

Step 9:策略二选一的对照试验(按需启/停)

A. 进一步压尾延迟(若观战仍卡顿)

# 禁用 LSO 试验
Disable-NetAdapterLso -Name "Ethernet 10G"
# 将 AutoTuning 调低,兼容老设备
Set-NetTCPSetting -SettingName Internet -AutoTuningLevelLocal Restricted

预期:尾延迟再降 5~10%,但 CPU 开销可能上浮 8~15%。

B. 回到吞吐优先(赛事回放/大房间推流)

# 保持 LSO,按需恢复 RSC
Set-NetAdapterRsc -Name "Ethernet 10G" -IPv4 Enabled -IPv6 Enabled
Set-NetTCPSetting -SettingName Internet -AutoTuningLevelLocal Normal

现场“坑点”与解法

X710 某版本驱动 + RSC 在高 PPS 下引入抖动

现象:p99 延迟忽高忽低,CPU 中断没上天但丢包上升。

解:禁用 RSC 后恢复稳定;后续在维护窗口升级驱动再复测。

TIME_WAIT 过多导致端口耗尽

现象:短时连接打满,SYN 被拒。

解:扩大动态端口区间 + 将 TcpTimedWaitDelay 降到 30s;对反向代理启用连接池。

某对端链路疑似 ICMP 限制导致 PMTUD 黑洞

现象:长时间握手卡住、重传率升高。

解:边界网关做 MSS clamping(TCP 端口 443/8443),应用层维持 TCP_NODELAY 与合理 SendBuffer。

应用层忘记开 Keep-Alive

现象:NAT/防火墙空闲回收,玩家感知“随机掉”。

解:系统全局 KA + 业务 Socket IOControl 精细化;双端一致。

安全软件实时扫描日志目录

现象:I/O 抖动影响处理线程,间接拉高延迟。

解:给日志目录加排除规则,I/O 热区与数据盘分离。

回滚与变更单(出问题 1 分钟内撤)

电源计划:powercfg /S SCHEME_BALANCED

RSS/RSC:

Disable-NetAdapterRss -Name "Ethernet 10G"
Set-NetAdapterRsc -Name "Ethernet 10G" -IPv4 Enabled -IPv6 Enabled

TCP 模板:

Set-NetTCPSetting -SettingName Internet -CongestionProvider Default `
  -ECNCapability Disabled -Timestamps Disabled -AutoTuningLevelLocal Normal

注册表:

reg import C:\reg_tcpip_bak.reg

动态端口/MTU:参照原值恢复。

最终成效(观察 24 小时的日报)
指标    高峰平均    非高峰平均
掉线率    0.42%    0.18%
TCP 重传率    0.6%    0.3%
p95 RTT    ~180 ms    ~140 ms
3 秒重连成功率    98.6%    99.1%

玩家舆情不再“刷屏”,赛事直播顺滑了。运维群里只剩零星的问题单。

附:我保留下来的“标准化操作清单”(SOP)

  1. 变更前:netsh int tcp show global、Get-NetTCPSetting、Get-NetAdapter* 拍快照;reg export 备份。
  2. 电源:切“终极性能”,锁最小处理器状态 100%。
  3. 网卡:启用 RSS(设置 Base/Max/NUMA),禁 RSC,LSO 先保留。
  4. TCP 模板:CUBIC + ECN + Timestamps + AutoTuning Normal。
  5. 端口/TIME_WAIT:扩大动态端口区间,TcpTimedWaitDelay=30。
  6. Keep-Alive:系统 15s/1s,业务 Socket 开 TCP_NODELAY 与 KA。
  7. SYN/重传:TcpMaxSynRetransmissions=4,TcpMaxDataRetransmissions=5。
  8. PMTUD/MTU:保持启用;必要时网关做 MSS Clamping。
  9. 验证:PktMon/PerfMon 指标对照;必要时 LSO on/off、AutoTuning Normal/Restricted 做 A/B。
  10. 回滚:一键导入注册表备份 + 恢复模板与网卡卸载能力。

凌晨两点,我端着机房自助咖啡,沿着冷风呼呼的走廊回到 NOC。大屏上那条掉线率曲线终于平稳贴着地面。

这些参数看似琐碎,背后是你对链路与业务形态的理解:跨境长肥管道、实时交互的尾延迟、NAT 与会话的寿命、以及内核如何响应拥塞信号。

做运维的浪漫,可能就是在最糟的时刻,用一组稳妥的系统级改动,让玩家“无感知地继续赢下那一局”。

参考取值与对照(便于拷贝到变更单)

类别 推荐值 说明
电源 计划 终极性能 降低频率抖动
网卡 RSS 开启,Base=2,Max=8 视核心数/NUMA 调
网卡 RSC 禁用 优先低延迟
网卡 LSO 开启(必要时禁用对比) 观察 CPU/尾延迟
TCP 拥塞算法 CUBIC 高 BDP 友好
TCP ECN Enabled 早期拥塞信号
TCP Timestamps Enabled 稳定 RTT 估计
TCP AutoTuning Normal(遇黑洞降 Restricted) 窗口自适应
端口 动态端口 TCP/UDP 10000–65535 缓解端口耗尽
连接 TIME_WAIT 30s 降低句柄压力
Keep-Alive 系统 15s / 1s 快速探测死链
SYN 重试 4 更快失败
重传 数据 5 默认即可
MTU PMTUD 开启 必要时网关 MSS

如果你的平台 UDP 协议占比更高(纯实时对战),在网关上加上 DSCP 标记(内部网段有效)与 队列优先级 会很香;公网不可控就别强求。

目录结构
全文