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

如何在香港服务器的 Windows Server 中配置 Receive Side Scaling(RSS),把多核 CPU 的网络性能“全部叫醒”?

发布人:Minchunlin 发布时间:2025-09-02 09:06 阅读量:1035


凌晨 2 点,我蹲在香港将军澳机房的42U机柜前,盯着一台新上架的 Windows Server 2022 物理服务器发呆:10GbE 光口明明协商正常,业务却怎么也跑不上 5Gbps。任务管理器里一条 CPU 核心 90%+,其他核几乎闲着,我当场就笑了——典型的 RSS 没配好。
这一晚,我把从排障到调优的过程都记了下来:不是“玄学”,是一步步可复现的实操。下面我用第一人称把它完整讲清楚——无论你是第一次摸 Windows 的网络栈,还是常年在 IDC 混的老兵,都能直接照做。

现场环境与原始症状

硬件 / 系统

项目 配置
机房 香港 TKO(双上联)
服务器 Dell R650
CPU Intel Xeon Silver 4314(16C/32T,单路)
内存 128GB DDR4
网卡 Intel X710-DA2(10GbE SFP+ ×2,驱动 1.13.x)
操作系统 Windows Server 2022 Datacenter(21H2,带桌面体验)
角色 物理机直连(无 vSwitch),跑 HTTP/2 服务与 gRPC(长连接多路复用)

问题特征

  • 单 TCP 流吞吐最高 ~2.8Gbps,多流总吞吐 ~4.2Gbps;
  • 任务管理器显示 0 号逻辑核接近满载,其它核心低负载;
  • perfmon 中 \Processor(0)% Processor Time 飙高,\Network Interface(X710)\Bytes Total/sec 明显低于链路上限。

结论几乎呼之欲出:网卡收包中断和协议栈处理基本“糊”在了单个核心上。在 Windows 上,这通常是 Receive Side Scaling(RSS) 没启、没配,或没把队列与 CPU 正确绑定。

原理一杯咖啡讲完:RSS 在 Windows 里怎么工作?

目的:把入方向的收包处理(中断、DPC、协议栈)按“流”(5 元组哈希)分散到多个 CPU 核,避免单核瓶颈。

关键部件

  • RSS 队列:NIC 上的多个接收队列(RX queues),每队列可独立触发中断。
  • RSS 哈希 & 重定向表(Indirection Table):按流哈希把流映射到队列/CPU。
  • CPU 亲和:为每个队列设置目标 CPU(BaseProcessorNumber 起始核,MaxProcessors 使用的核数)。
  • NUMA 亲和:单路 CPU 也有 L3 共享/本地性;多路或 >64 逻辑核时,还涉及 Processor Group 与 NumaNode,需要显式设定,避免跨节点/跨 Group 访问造成额外延迟。

一句话:队列要多、核要分、亲和要对。

动手前的“3 分钟预检”

# 1) 看网卡与状态
Get-NetAdapter | ? Status -eq 'Up' | ft Name, InterfaceDescription, LinkSpeed

# 2) 看 RSS 全局与 NIC 粒度状态
netsh int tcp show global | findstr /i "Receive-Side Scaling"
Get-NetAdapterRss -Name "Ethernet 2"  # 把名字换成你的 X710 口

# 3) 看 CPU/NUMA 拓扑
Get-CimInstance Win32_Processor | select Name,NumberOfCores,NumberOfLogicalProcessors
Get-NetAdapterRss -Name "Ethernet 2" | fl *  # 观察 NumaNode, BaseProcessorNumber, MaxProcessors

期望看到:

  • Receive-Side Scaling State : enabled;
  • X710 口 Enabled : True;
  • NumberOfReceiveQueues ≥ 4;
  • MaxProcessors 至少 4~8;
  • NumaNode 合理(单路通常是 0)。

如果全都不合格,下面开始“对症下药”。

配置步骤:把队列、核和亲和一口气配到位

1)开启或确认 RSS 已启

# 全局(一般默认启用)
netsh int tcp set global rss=enabled

# 针对网卡口(很关键)
Enable-NetAdapterRss -Name "Ethernet 2"
Get-NetAdapterRss -Name "Ethernet 2"

若你看到 Enabled : False,多数是驱动/高级属性里被关了,或该口被绑定到奇怪的“配置文件”。

2)把接收队列数量拉满(取决于 NIC 能力)

不同驱动的“高级属性”显示名不一致,先把它们列出来:

Get-NetAdapterAdvancedProperty -Name "Ethernet 2" |
  sort DisplayName | ft DisplayName, DisplayValue -AutoSize

常见关键词:“RSS Queues / Receive Side Scaling Queues / #RSS Queues”。把它设到一个合理的值(X710 通常 4/8 没问题):

# 示例:设置为 8 个接收队列
Set-NetAdapterAdvancedProperty -Name "Ethernet 2" `
  -DisplayName "RSS Queues" -DisplayValue "8 Queues"

有些驱动用“Receive Buffers(接收环形缓冲)”限制并发度,建议一并调大到 2048~4096。

Set-NetAdapterAdvancedProperty -Name "Ethernet 2" `
  -DisplayName "Receive Buffers" -DisplayValue "4096"

3)把 RSS 映射到一段连续 CPU 核(避开 0 号核)

经验上我会避开 0 号核(系统/中断容易“粘”在它上面),从 2 或 4 开始:

# 用 8 个逻辑核,从第 2 号逻辑核开始
Set-NetAdapterRss -Name "Ethernet 2" `
  -BaseProcessorNumber 2 -MaxProcessors 8
Get-NetAdapterRss -Name "Ethernet 2" | fl BaseProcessorNumber,MaxProcessors,MaxProcessorNumber

多于 64 逻辑核的机器请额外指定 Processor Group,否则只会用到 Group 0 的核:

Set-NetAdapterRss -Name "Ethernet 2" `
  -BaseProcessorGroup 0 -MaxProcessorGroup 0 `
  -BaseProcessorNumber 2 -MaxProcessors 16

NUMA 注意:若是双路/多 NUMA,优先把 RSS 绑定到与该 NIC 直连的 NUMA 节点(通常是 0 或 1):

Set-NetAdapterRss -Name "Ethernet 2" -NumaNode 0

4)RSC/LSO 等卸载项:一般保持开启

RSC(Receive Segment Coalescing):减少每包处理开销,对长连接吞吐更友好;

LSO/Checksum Offload:对发送路径/校验和卸载有效,一般保持开启。

检查/调整:

Get-NetAdapterAdvancedProperty -Name "Ethernet 2" | ? DisplayName -match "RSC|LSO|Checksum"
# 如需改:
# Set-NetAdapterAdvancedProperty -Name "Ethernet 2" -DisplayName "IPv4 Checksum Offload" -DisplayValue "Enabled"

5)中断调制(Interrupt Moderation)

过高会加大延迟、降低包率;过低会让 CPU 被中断“打爆”。X710 的“Interrupt Moderation Rate”我一般设为 Adaptive 或 Medium,并结合实测再微调:

Set-NetAdapterAdvancedProperty -Name "Ethernet 2" `
  -DisplayName "Interrupt Moderation Rate" -DisplayValue "Adaptive"

6)电源与 BIOS

Windows 电源计划改为 高性能:

powercfg /S SCHEME_MIN

BIOS 里尽量关闭深度 C-States(视业务而定),确保 PCIe ASPM 不影响中断延迟。

压测:用 ntttcp/iperf3 看“术后对比”

工具选择

微软出品 NTTTCP 更贴近 Windows 网络栈特性,多流/亲和好用;

也可以用 iperf3 辅助观测多流行为。

准备:对端找一台同样 10GbE 的测试机(同机房或跨机房都可以),时间同步,确保链路与 MTU(1500/9000)一致。

NTTTCP 示例

被测服务器(接收端):

# 16 条接收流,持续 60s
.\ntttcp.exe -r -m 16,*,192.168.10.10 -t 60

发送端:

# 16 条发送流,窗口合适即可
.\ntttcp.exe -s -m 16,*,192.168.10.10 -t 60

iPerf3 示例

  • 接收端:iperf3 -s
  • 发送端:iperf3 -c 192.168.10.10 -P 16 -t 60

我现场的对比数据

指标 调优前 调优后
多流总吞吐(NTTTCP) 4.2 Gbps 9.6 Gbps
单流(iPerf3 -P 1) 2.8 Gbps 5.3 Gbps
逻辑核最高占用 92%(Core#0) 55%(Core#2~9 平均分摊)
DPC 队列长度峰值 2.8 0.7
丢包(交换机口计) 偶发 趋近 0

验证脚本(采样 CPU 与网络计数器):

# 采样 60s 的关键计数器
$ctrs = @(
  '\Processor(_Total)\% Processor Time',
  '\Processor(2)\% Processor Time','\Processor(3)\% Processor Time','\Processor(4)\% Processor Time',
  '\Network Interface(*)\Bytes Total/sec',
  '\TCPv4\Segments/sec'
)
Get-Counter -Counter $ctrs -SampleInterval 1 -MaxSamples 60 |
  Select -ExpandProperty CounterSamples |
  Select TimeStamp,Path,CookedValue |
  Format-Table -AutoSize

常见坑与我的“现场解法”

RSS 全局启了,但 NIC 口还是不分核

现象:netsh int tcp show global 是 enabled,Get-NetAdapterRss 却显示 Enabled : False。

解法:Enable-NetAdapterRss -Name <NIC>;若仍失败,更新驱动到厂商版本(Windows 盒装驱动有时阉割高级属性)。

队列设了很多,吞吐反而不稳

现象:抖动、丢包增多、CPU 抖动。

解法:适当减少队列(例如 8→4)、把 Interrupt Moderation 设为 Adaptive,并把 Receive Buffers 拉大到 4096。

>64 逻辑核的机器只吃 Group 0

现象:单 Group 满载,其他 Group 闲着。

解法:显式设置 -BaseProcessorGroup/-MaxProcessorGroup;必要时按 NUMA 分组绑定不同 NIC 口,做到“一口一组”。

Hyper-V 场景 VMQ/vRSS 冲突认知

要点:物理 NIC 上建议 开启 VMQ;VM 内网卡上 启用 vRSS,才能把队列从外层分到来宾多核。

# 宿主
Enable-NetAdapterVmq -Name "vEthernet (External)"
# 来宾
Set-VMNetworkAdapter -VMName "AppVM-01" -VrssEnabled $true -VmqWeight 100

若你不是虚拟化场景,不要纠结 VMQ,专注 RSS 即可。

Windows 更新后驱动把高级属性“洗掉”

现象:吞吐忽降,查看发现队列/亲和回到了默认。

解法:把配置写成开机脚本固化,或者在任务计划里“网络上线后”运行一遍(下面给出脚本)。

MTU 与对端不一致

现象:大包分片、吞吐起不来。

解法:统一 MTU(1500 或 9000),交换机/对端/主机三端一致。

netsh interface ipv4 set subinterface "Ethernet 2" mtu=1500 store=persistent

一键固化:我在现场留下的 PowerShell 脚本

下面这个脚本我放在 C:\ops\rss-tune.ps1,配合任务计划(系统启动后 1 分钟运行):

$nic = "Ethernet 2"            # 换成你的口名
$base = 2                      # 从第 2 号逻辑核开始
$procs = 8                     # 使用 8 个逻辑核
$rxQueues = "8 Queues"
$rxBuf = "4096"
$imr = "Adaptive"

function Wait-AdapterReady($name){
  for($i=0;$i -lt 30;$i++){
    $a = Get-NetAdapter -Name $name -ErrorAction SilentlyContinue
    if($a -and $a.Status -eq "Up"){ return }
    Start-Sleep -Seconds 2
  }
  throw "Adapter $name not ready"
}

Wait-AdapterReady $nic

Enable-NetAdapterRss -Name $nic -ErrorAction SilentlyContinue
Set-NetAdapterRss -Name $nic -BaseProcessorNumber $base -MaxProcessors $procs

# 高级属性:队列、缓冲、中断调制(不同驱动显示名可能不同,失败会被忽略)
$props = Get-NetAdapterAdvancedProperty -Name $nic
if($props | ? DisplayName -match 'RSS Queues'){
  Set-NetAdapterAdvancedProperty -Name $nic -DisplayName "RSS Queues" -DisplayValue $rxQueues -ErrorAction SilentlyContinue
}
if($props | ? DisplayName -match 'Receive Buffers'){
  Set-NetAdapterAdvancedProperty -Name $nic -DisplayName "Receive Buffers" -DisplayValue $rxBuf -ErrorAction SilentlyContinue
}
if($props | ? DisplayName -match 'Interrupt Moderation Rate'){
  Set-NetAdapterAdvancedProperty -Name $nic -DisplayName "Interrupt Moderation Rate" -DisplayValue $imr -ErrorAction SilentlyContinue
}

# 打印结果
Get-NetAdapterRss -Name $nic | fl Name,Enabled,BaseProcessorNumber,MaxProcessors,MaxProcessorNumber,NumberOfReceiveQueues,NumaNode

验收清单(上线前最后 5 分钟)

  •  Get-NetAdapterRss 显示 Enabled=True、MaxProcessors 合理;
  •  高级属性里 RSS Queues=预期值、Receive Buffers≥2048;
  •  任务计划中固化脚本已添加,系统重启后配置仍在;
  •  ntttcp/iperf3 多流吞吐 ≥ 9Gbps(同机房 10GbE)或达到跨机房预期;
  •  perfmon 中各核心负载均衡,DPC 长度无异常峰值;
  •  交换机口无递增丢包,FEC/错误包计数稳定。

从“一个核干到秃”到“八仙过海,各显神通”

那晚我在机房把最后一条命令敲下去,又跑了一把 ntttcp,看着吞吐从 4Gbps 一路顶到 9.6Gbps,任务管理器里几条 CPU 核均匀地跳动,心里那口气也顺了。
RSS 不是神秘开关,它很“实”,就藏在网卡驱动与 Windows 的几个参数里。把队列数拉起来,把核的亲和绑正确,把缓冲与中断调平衡,你就能把多核的潜力稳稳接住。

下回你在香港的机房里遇到“单核拉满、吞吐上不去”的怪象,想想今天这套流程。也许你会像我一样,听着空调的风声,笑着把那条顽固的 0 号核“从一线调回幕后”,让整台服务器真正跑起来。

目录结构
全文