如何在香港服务器的 Windows Server 2016 上调整 TCP Chimney Offload,避免高延迟网络下的性能下降

那天已经接近午夜,我坐在香港葵涌机房的冷通道里,抱着一台临时接上的笔记本,对着一台装着 Windows Server 2016 的物理服务器发呆。白天客户还在抱怨:“大文件从内地传到香港特别慢,经常断流,SMB 复制速率忽上忽下。”网络拨测 RTT 在 65–95ms 之间,按理说带宽 1–2Gbps 的跨境专线做长肥管道(LFN)优化应该能跑起来,但实际表现惨不忍睹。
我把工单翻了个遍,发现一个细节:这台机是几年前做的金镜像自动化部署,后来补丁滚了一轮又一轮,但TCP Chimney Offload 的策略却被一份古早 GPO 残留给“动过手”。这玩意儿在 Windows 时代变迁里地位微妙:理论上把 TCP 处理从主机栈卸到网卡,实际上很多驱动/路径组合在高延迟场景反而会抖。于是我做了一次“外科手术”——把 Chimney 关掉,配合 RSS、RSC 和 TCP 自动调谐做了一套系统性调优。下面是完整复盘。
我的现场环境与关键参数
- 位置/线路:HK 机房(葵涌),上联 ToR:Arista 7050 系列,IDC 内部 10GbE,跨境专线 2×1Gbps 叠加,业务常见 RTT(华南 ↔ HK)约 65–95ms,去美西公网 RTT 140–180ms。
- 主机:Dell PowerEdge 2U(双路 Xeon,128GB RAM)
- 网卡:双口 10GbE(Intel X710-DA2),单口与 vLAN 业务平面绑定
- 系统:Windows Server 2016 Standard(1607,长期打补丁)
典型业务:
- SMB 文件分发(数十 GB 备份/镜像)
- IIS 静态大对象下载(>1GB)
- RDP/WinRM 管理面
问题描述:高 RTT 路径下吞吐上不去、速率锯齿、偶发短暂无流量(客户感知“断流”)
先说结论(给赶时间的你)
在 Windows Server 2016 上,优先关闭** TCP Chimney Offload**(全局 + 网卡),启用/核对 RSS、RSC、自动窗口调谐(Auto-Tuning)。
对高延迟且带宽充足的链路,考虑 Auto-Tuning = normal 或 experimental(需 A/B 实测);ECN 视路径设备支持再决定。
对某些老驱动/设备组合,禁用 LSO v2(LSO/GSO)能消除锯齿;新驱动能正常则保持开启以减轻 CPU。
所有改动需要配套验证:iperf3/robocopy/curl -w,并抓取 重传率、拥塞窗口 和 CPU 软中断 的变化。
原理速写:为什么要动 Chimney?
TCP Chimney Offload 的目标是把 TCP 会话处理卸载给 NIC/驱动(或半卸载),减轻 CPU,但在高 RTT/复杂路径里,卸载栈与主机 TCP 栈的行为偏差、驱动实现差异、以及某些中间设备的“奇葩”行为,会放大重传、乱序、窗口收缩等现象,表现为吞吐抖动和锯齿。
Windows Server 2016 时代,微软更推 RSS/RSC + 主机栈优化的路径。RSS(多核并行处理)、RSC(合并段减少包处理开销)与自动窗口调谐配合,通常比 Chimney 更稳。
高 BDP(带宽×时延)链路要大窗口才能跑满带宽;窗口调谐与拥塞控制策略(如 CTCP)在主机栈里更易于被正确管控与观测。
变更前检查(别上来就改)
1)查看全局 TCP/Offload 状态(双命令系路线)
# PowerShell 路线(推荐)
Get-NetTCPSetting | ft SettingName, CongestionProvider, AutoTuningLevelLocal
Get-NetOffloadGlobalSetting
Get-NetAdapterRss
Get-NetAdapterAdvancedProperty -Name "*10G*" | ft DisplayName,DisplayValue
# netsh 路线(通用)
netsh int tcp show global
netsh int ip show offload
关注这些字段:
- Chimney Offload State
- Receive-Side Scaling (RSS) State
- Receive Segment Coalescing (RSC) State
- Add-On Congestion Control Provider(在 2016 上通常可设为 CTCP)
- Receive Window Auto-Tuning Level(disabled/restricted/normal/highlyrestricted/experimental)
2)采集基线数据
连通性/路径
# Windows 客户端/跳板上
ping <HK_SERVER_IP> -n 50
tracert <HK_SERVER_IP>
iperf3(建议双向)
# 服务器端
iperf3 -s
# 客户端(华南 IDC 或云主机)
iperf3 -c <HK_SERVER_IP> -p 5201 -t 60 -R --logfile iperf_before.log
SMB 拷贝
robocopy \\HK-SERVER\share C:\test\ /E /R:0 /W:0 /TEE /LOG:robo_before.log
HTTP 大对象
curl -o NUL -w "time_total:%{time_total}\nsize_download:%{size_download}\nspeed_download:%{speed_download}\n" http://<hk>/bigfile.img
实操步骤:从 Chimney 到稳态主机栈
建议在变更窗口操作,准备回滚手段。操作包含全局参数与网卡高级属性两层。
Step 1:全局关闭 TCP Chimney,校正主机栈策略
# 1) 关 Chimney
Set-NetOffloadGlobalSetting -Chimney Disabled
# netsh 等价:
# netsh int tcp set global chimney=disabled
# 2) 开 RSS(多核并行)
Enable-NetAdapterRss -Name "*10G*"
# 3) 开/核实 RSC(2016 原生对物理 NIC 的 RSC 支持因驱动而异,虚拟交换场景更常见)
Set-NetAdapterAdvancedProperty -Name "*10G*" -DisplayName "Receive Segment Coalescing (RSC)" -DisplayValue "Enabled" -ErrorAction SilentlyContinue
# 4) TCP 自动窗口调谐
netsh int tcp set global autotuninglevel=normal
# 若 BDP 很大且路径稳定,可试 experimental(务必 A/B 测):
# netsh int tcp set global autotuninglevel=experimental
# 5) 拥塞控制(2016 可用 CTCP)
netsh int tcp set global congestionprovider=ctcp
# 6) 视中间设备支持决定 ECN(默认关闭较稳)
# netsh int tcp set global ecncapability=enabled # 仅在确认全路径支持时开启
提示:部分环境历史遗留可能通过注册表写过 EnableTCPChimney(HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters)。优先用 Set-NetOffloadGlobalSetting / netsh,如需清理,确保重启窗口与回滚计划。
Step 2:网卡高级属性对齐(驱动/固件很关键)
打开设备管理器 → 网络适配器 → 你的 10GbE 网卡 → 高级,建议:
- RSS:Enabled(队列数视 CPU 设 8/16/32)
- Interrupt Moderation:Enabled/Adaptive
- Receive/Transmit Buffers:调高(如 1024/2048)
- Large Send Offload v2 (IPv4/IPv6):优先保持 Enabled(新驱动稳定时减少 CPU);若观测到高 RTT 场景锯齿/断流,可临时置 Disabled 做 A/B
- Checksum Offload:通常 Enabled(同上,个别古老设备链路可测后决定)
- Jumbo Frame:仅端到端一致时开启(IDC 内网可 9000;公网/跨域链路保持 1500)
PowerShell 等效批量(示例):
$nic = "*10G*"
Enable-NetAdapterRss -Name $nic
Set-NetAdapterAdvancedProperty -Name $nic -DisplayName "Interrupt Moderation" -DisplayValue "Enabled"
Set-NetAdapterAdvancedProperty -Name $nic -DisplayName "Receive Buffers" -DisplayValue "2048" -ErrorAction SilentlyContinue
Set-NetAdapterAdvancedProperty -Name $nic -DisplayName "Transmit Buffers" -DisplayValue "2048" -ErrorAction SilentlyContinue
# 如需 A/B:
# Set-NetAdapterAdvancedProperty -Name $nic -DisplayName "Large Send Offload v2 (IPv4)" -DisplayValue "Disabled"
# Set-NetAdapterAdvancedProperty -Name $nic -DisplayName "Large Send Offload v2 (IPv6)" -DisplayValue "Disabled"
Step 3:重启网络栈或计划性重启
# 最稳是维护窗重启主机
Restart-Computer
# 无法重启时,重置 NIC(注意影响在线业务)
Disable-NetAdapter -Name "*10G*" -Confirm:$false
Start-Sleep -Seconds 3
Enable-NetAdapter -Name "*10G*"
验证与数据对比
我分别对 华南↔HK(RTT 70–90ms) 与 HK↔美西(RTT 150–170ms) 做了 3 轮对比,统计窗口 60 秒,结果抽样如下(单位简化):
1)iperf3(接收方向,-R)
| 场景 | 调整前(疑似 Chimney 在作祟) | 调整后(Chimney Disabled + RSS/RSC+Auto-Tuning) |
|---|---|---|
| 华南↔HK | 210–380 Mbps,重传率 1.8–3.2%,速率呈锯齿 | 820–980 Mbps,重传率 <0.5%,速率平滑 |
| HK↔美西 | 90–160 Mbps,偶发 3–5 秒“停顿” | 320–420 Mbps,无明显停顿 |
iperf3 典型命令:iperf3 -c <HK_IP> -p 5201 -t 60 -R
2)SMB(robocopy 单文件 >10GB)
| 场景 | 调整前 | 调整后 |
|---|---|---|
| 华南↔HK | 24–45 MB/s,日志见大量重试 | 95–115 MB/s,无异常重试 |
| HK↔美西 | 12–20 MB/s | 38–55 MB/s |
3)HTTP 大对象下载(curl -w)
| 指标 | 调整前 | 调整后 |
|---|---|---|
speed_download |
140–320 Mbps | 700–950 Mbps |
time_total(1GB) |
28–60 s | 8–12 s |
4)CPU/软中断变化(PerfMon)
| 计数器 | 调整前 | 调整后 |
|---|---|---|
Processor(_Total)\% Privileged Time |
18–24% | 10–14% |
Network Interface\Packets/sec 抖动 |
明显 | 平滑 |
TCPv4\Segments Retransmitted/sec |
高峰值 > 1800 | 稳定 < 300 |
常见坑与现场解决
“我明明在 2016 上看不到 Chimney 影响?”
有些版本/补丁默认已是 disabled,但历史 GPO/镜像脚本可能又把它打开或写过注册表;更常见的是网卡 TOE/LSO 组合引发类似症状。结论:不要想当然,一定用命令看实际状态,并做 A/B。
RSC 在物理 NIC 上看不到?
与驱动/固件相关,部分只在 vSwitch/vNIC 场景生效。没关系,关键是RSS + 窗口调谐,RSC 只是锦上添花。
LSO v2 开着时吞吐锯齿
老驱动偶发。先确认已关 Chimney,再对 LSO v2 做 A/B。我的现场中,美西高 RTT 路径关掉 LSO 后速率稳定性更好,但 CPU 增加了 2–4%。可接受的话就这么用,或升级驱动再回开。
ECN 开了更差
中间设备对 ECN 支持不一致,默认别开。只有确认端到端设备都支持时再启用。
拥塞控制到底用谁?
在 2016 上把 congestionprovider=ctcp 通常是正解。新系统(如 2019+)默认拥塞算法变化更大,另当别论。
注册表修过忘了重启
netsh/PowerShell 的大多数全局项即时生效,但注册表项需要重启才可 100% 一致。生产上要么只走 Set-*/netsh,要么安排重启。
一键回滚(出问题时)
# 回滚主机栈
netsh int tcp set global chimney=default # 或 disabled → default
netsh int tcp set global autotuninglevel=normal
netsh int tcp set global congestionprovider=none
# 网卡相关(按需)
Set-NetAdapterAdvancedProperty -Name "*10G*" -DisplayName "Large Send Offload v2 (IPv4)" -DisplayValue "Enabled"
Set-NetAdapterAdvancedProperty -Name "*10G*" -DisplayName "Large Send Offload v2 (IPv6)" -DisplayValue "Enabled"
# 如修改过 RSS/RSC 再相应回调
生产经验:保留变更前导出
netsh int tcp show global > C:\prechange_tcp.txt
Get-NetOffloadGlobalSetting | Out-File C:\prechange_offload.txt
Get-NetAdapterAdvancedProperty -Name "*10G*" | Export-Csv C:\prechange_nic.csv -NoTypeInformation
最后给你一份可复制的“高延迟链路优化清单”
- Chimney Offload 全局 Disabled
- RSS Enabled(队列合理)
- Auto-Tuning = normal(必要时试 experimental 并 A/B)
- congestionprovider=ctcp(2016 推荐)
- RSC 能开则开(不强求)
- LSO v2 先保持 Enabled,遇到锯齿再 A/B
- Buffer/Interrupt Moderation 优化
- Jumbo 仅端到端一致才开
- iperf3/robocopy/curl 三板斧验证 + PerfMon 观测
- 预留回滚与日志留存
现场日志片段(摘抄)
netsh int tcp show global 变更前:
Chimney Offload State : automatic
Receive-Side Scaling State : enabled
Receive Window Auto-Tuning Level : restricted
Add-On Congestion Control Provider : none
变更后:
Chimney Offload State : disabled
Receive-Side Scaling State : enabled
Receive Window Auto-Tuning Level : normal
Add-On Congestion Control Provider : ctcp
凌晨两点出头,我在冷通道里把最后一轮 iperf3 的日志收好,曲线终于像心电图回了正态,客户那头的运维在电话里“咦”了一声:“刚才那批镜像,速度稳了,连断流都没了。”我合上笔记本,抬头看着机柜上那串熟悉的资产标签,忽然想起几年前第一次在这台机子前做上线——同一台服务器、不同的操作系统策略,会带来完全不同的网络气质。
后来我把这个变更整理成标准化 Runbook:先关 Chimney,再让 RSS/RSC 和主机栈发力,必要时对 LSO/ECN/窗口调谐做 A/B。每次跨境、跨洋的长肥管道优化,我都先从这张清单开始。它不是万能钥匙,但大多数时候,它能把问题从“玄学”拉回到工程学。
如果你也正被“高延迟 + 吞吐抖动”折磨,按上面的步骤走一遍。别迷信某个单点开关,网络优化是系统工程——只要数据说话,你就会在日志里亲眼看到它变好。祝你那一夜,也能早点下班。