香港服务器上的Windows Server系统中,如何设置和优化QoS策略,保障实时对战类游戏的数据传输优先级?

那天夜里,我刚把一批 Windows Server 2022 的补丁打完没多久,客服就把微信甩过来:“跨区对战抖成 PPT,广州和台北的玩家都在骂延迟飙。”
我用 Zabbix 和交换机队列看了眼,出向链路并不满,但 1 分钟内出现了几次短时突发(microburst)。再翻 Windows 侧的流量,发现实时对战的 UDP 小包被同机的回放上传、日志归档、镜像分发这些“笨重的 TCP”挡在队尾。那一刻我决定:不再赌网络空闲,直接在 Windows Server 上用 QoS 把实时通道抬成头等舱。
基线环境与约束
硬件与系统(真实在跑的那批)
- 机型:Dell PowerEdge R650(1U)
- CPU:Intel Xeon Silver 4314 × 1(16C/32T)
- 内存:128GB DDR4-3200
- 系统盘:Intel D3-S4510 480GB(SATA)
- 业务盘:Samsung PM9A3 1.92TB × 2(NVMe,Raid1)
- 网卡:Intel X710-DA2(双口 10GbE,直连 ToR)
- 操作系统:Windows Server 2022 Datacenter(21H2,已打到当时最新补丁)
- 虚拟化:无 Hyper-V(这台是物理直跑游戏进程;文中也给 Hyper-V 的做法)
- 交换机:ToR 是 25G 上联的 Arista(本段只用到L2/L3 DSCP,不依赖厂商特性)
网络拓扑
上联:HKT/NTT 双线,策略路由分省份;IDC 内部三层架构
关键点:跨运营商段不保证保留 DSCP,但 机房边界和 IDC 内部我们能识别与优先转发
我们的业务端口规划(示例,可替换成你的)
| 流量类型 | 协议/端口 | 方向 | 说明 |
|---|---|---|---|
| 实时对战(状态同步) | UDP 30000–30100 | 双向 | 66–120B 小包,高 PPS |
| 实时语音(内置) | UDP 31000–31010 | 双向 | 抖动敏感 |
| 匹配/房间 | TCP 21000 | 出站为主 | 建连与少量信令 |
| 观战回放上传 | TCP 28080 | 出站 | 大对象、可延迟 |
| 日志/分析上报 | TCP 29000 | 出站 | 批量,非实时 |
| 运营后台/API | TCP 443 | 双向 | 常规 Web |
QoS 设计思路(我当晚的决策)
DSCP 标记:
- 实时状态(UDP 30000–30100)→ EF(46)
- 语音(UDP 31000–31010)→ AF41(34)
- 匹配/信令(TCP 21000)→ AF31(26)
- 回放/日志等挤占型流量 → CS1(8)(背景级,必要时限速)
最小带宽保障 + 队列优先级(可选,网卡与交换机支持时启用):
- 在 Windows + X710 驱动 侧开启 Adapter QoS + ETS,把 EF 类别至少预留 30% 的发包口粮,避免被 TCP 拖尾。
不依赖公网的 DSCP 诉求:
- 出 IDC 之后对方可能清洗 DSCP,但机房内/边界优先队列已足够抗住本端的突发。这点在我们的延迟 P99 里非常关键。
实施 A:单机用 PowerShell 下发 QoS(最快落地)
适用于临时加急、或你在做金丝雀验证。不需要域控。
清理试验机的旧策略(只动本地存储)
# 只查看本地策略,不动组策略
Get-NetQosPolicy -PolicyStore Local | Format-Table -Auto
# 如需清空旧规则(谨慎)
Get-NetQosPolicy -PolicyStore Local | Remove-NetQosPolicy -PolicyStore Local -Confirm:$false
创建 DSCP 策略
# 1) 实时对战:EF(46)
New-NetQosPolicy -Name "Game-RT-UDP" `
-PolicyStore Local `
-IPProtocolMatchCondition UDP `
-DestinationPortMatchCondition 30000-30100 `
-DSCPAction 46 -NetworkProfile All
# 2) 实时语音:AF41(34)
New-NetQosPolicy -Name "Game-Voice-UDP" `
-PolicyStore Local `
-IPProtocolMatchCondition UDP `
-DestinationPortMatchCondition 31000-31010 `
-DSCPAction 34 -NetworkProfile All
# 3) 匹配/信令:AF31(26)
New-NetQosPolicy -Name "Game-Match-TCP" `
-PolicyStore Local `
-IPProtocolMatchCondition TCP `
-DestinationPortMatchCondition 21000 `
-DSCPAction 26 -NetworkProfile All
# 4) 观战回放(背景级):CS1(8) + 可选限速
New-NetQosPolicy -Name "Game-Replay-Background" `
-PolicyStore Local `
-IPProtocolMatchCondition TCP `
-DestinationPortMatchCondition 28080 `
-DSCPAction 8 `
-ThrottleRateActionBitsPerSecond 200Mb
说明:
- -NetworkProfile All 覆盖域/专用/公用场景。
- ThrottleRateActionBitsPerSecond 给背景类一个上限(上例 200Mb),防止高峰期把队列打爆。
- 如果你是源端口更稳定,可改用 -SourcePortMatchCondition。
- 如果你想绑定到进程路径(如 C:\game\server.exe),可用 -AppPathNameMatchCondition。
查看生效情况
Get-NetQosPolicy -PolicyStore Local | Format-List
实施 B:批量推送(域控 GPO / 本地策略)
生产里我用 GPO(Policy-based QoS) 批量下发。优点:可视化、审计、回滚简单。
本地策略操作(单机演示,域控同理)
打开 gpedit.msc → “计算机配置” → “Windows 设置” → “基于策略的 QoS”
右键新建策略,填:
- 策略名称:Game-RT-UDP
- 指定 DSCP 值:46;不勾选限速(实时流量不限速)
- 此 QoS 策略适用的应用程序:留空(按端口匹配)
- 此 QoS 策略适用的 IP 地址:任意
- 此 QoS 策略适用的协议和端口:选择 UDP,目标端口 30000-30100
用同样方法新建 Game-Voice-UDP(34), Game-Match-TCP(26), Game-Replay-Background(8, 可选限速)。
gpupdate /force,重启相关服务或淡出换班时重启机器。
注意:PowerShell 与 GPO 最终都进 ActiveStore,但存储位置不同。不要混着改,以免定位困难。
实施 C(可选):开启网卡 Adapter QoS + ETS 保障(X710/Mellanox 等)
这步是加分项,能在Windows→NIC→ToR这一跳把 EF 流量的最小口粮钉住。需要网卡/驱动支持 DCB/ETS;交换机若支持更佳。
# 1) 打开网卡级 QoS
Enable-NetAdapterQos -Name "Ethernet"
# 2) 设置 DCBX 为 “愿意(Willing)”(交换机会下发也可)
Set-NetQosDcbx -Willing $True
# 3) 建立流量类:优先级5(常用于 EF),预留30%
New-NetQosTrafficClass -Name "TC_EF" -Priority 5 -BandwidthPercentage 30 -Algorithm ETS
# 4) 将 DSCP=46 的包同时打上 802.1p 优先级5(数据链路内有效)
Set-NetQosPolicy -Name "Game-RT-UDP" -PolicyStore Local -PriorityValue8021Action 5
网卡高级属性建议(X710)
- Interrupt Moderation:Low 或 Off(纯低延迟场景);高 PPS 时 Off 会多吃 CPU,自行权衡
- RSS Queues:与物理核心数匹配(如 8)
- Receive/Transmit Buffers:适度调高(如 1024/1024),防短突发丢包
- Energy Efficient Ethernet:关闭
- VLAN Offload/DSCP Preserve:开启保留(不同驱动名称略有差异)
Hyper-V 场景(如果你的游戏跑在 VM 里)
在 vSwitch 上启用最小带宽权重(隔离背景型 VM)
# 例:把实时游戏VM的权重调高
Set-VMNetworkAdapter -VMName "Game-VM-01" -MinimumBandwidthWeight 80
Set-VMNetworkAdapter -VMName "Log-VM-01" -MinimumBandwidthWeight 10
DSCP 端到端保留:
- 在 宿主 和 来宾都按上文建 QoS(GPO 更方便)
- 新版 vSwitch 会保留 DSCP,若你的版本或某些虚拟化层会清洗,需在宿主侧再做一次 DSCP 标记兜底。
验证与观测(我当晚是这么确认的)
抓包看 DSCP 是否生效
服务器上跑 Wireshark 或 npcap + windump,过滤:
(udp.port >= 30000 && udp.port <= 30100) && ip.dsfield.dscp == 46
观察发包路径,确认 DSCP=46 已打上
交换机/路由器侧
- ToR 上看队列统计(以厂商命令为准),确认 EF/AF41 队列在突发期无明显丢包、tx-ring 不堆积
Windows 性能计数器
- Perfmon → Network Interface:Output Queue Length(应 ≤1)、Packets/sec
- Processor:DPC Rate(不要持续拉高)
- UDPv4:Datagrams/sec(验证 PPS 峰值)
对比数据(当晚抓的一个 15 分钟窗口)
| 指标 | 调整前 | 调整后 |
|---|---|---|
| 实时对战 RTT 中位数 | 19.4 ms | 17.8 ms |
| RTT P99 | 118.6 ms | 43.2 ms |
| 服务器出向丢包(EF 队列) | 0.12% | 0.01% |
| 观战回放平均速率 | 680 Mbps | ~190–210 Mbps(被限速) |
| 玩家断连率(同时段) | 0.9% | 0.3% |
常见坑 & 处理笔记(这些都是我踩过的)
“我打了 DSCP 还是没用?”
很多公网段会清洗 DSCP。别纠结对端;只要机房内/边界优先,你已经把 80% 的突发影响打掉了。
网卡驱动“吃掉 DSCP”
少数旧驱动在封装/分片时会没保留 DSCP。升级 X710 驱动(同步 NVM)后恢复,且把 DSCP Preserve/Trust 打开。
把实时流量也限速了
别勾“限速”在实时策略上。Throttle 只用在回放、日志、镜像分发这类“能等的”。
GPO 和本地策略打架
一台机器用一种来源。定位时看 Get-NetQosPolicy -PolicyStore ActiveStore 与 -PolicyStore Local/GroupPolicy 的差异。
Hyper-V 里 DSCP 丢失
老版本 vSwitch 有保留问题;我在宿主再下发一次基于端口的 DSCP 兜底,就不丢了。
队列预留太激进
ETS 预留不是越大越好。EF 30% 已经很稳;我试过 50%,反而把 Web/API 挤得不太顺畅。
完整脚本(单机本地策略,一键创建/回滚)
create-qos.ps1
# 创建本地 QoS 策略
$pols = @(
@{Name="Game-RT-UDP"; Store="Local"; Proto="UDP"; DstPorts="30000-30100"; DSCP=46; Throttle=$null},
@{Name="Game-Voice-UDP"; Store="Local"; Proto="UDP"; DstPorts="31000-31010"; DSCP=34; Throttle=$null},
@{Name="Game-Match-TCP"; Store="Local"; Proto="TCP"; DstPorts="21000"; DSCP=26; Throttle=$null},
@{Name="Game-Replay-Background"; Store="Local"; Proto="TCP"; DstPorts="28080"; DSCP=8; Throttle=200Mb}
)
foreach($p in $pols){
if(Get-NetQosPolicy -PolicyStore $p.Store -ErrorAction SilentlyContinue | ? Name -eq $p.Name){
Write-Host "Exists: $($p.Name) -> skip"
continue
}
$args = @{
Name = $p.Name
PolicyStore = $p.Store
IPProtocolMatchCondition = $p.Proto
DestinationPortMatchCondition = $p.DstPorts
DSCPAction = $p.DSCP
NetworkProfile = 'All'
}
if($p.Throttle){ $args['ThrottleRateActionBitsPerSecond'] = $p.Throttle }
New-NetQosPolicy @args | Out-Null
Write-Host "Created: $($p.Name)"
}
# 可选:开启网卡 QoS 与 ETS(如需)
# Enable-NetAdapterQos -Name "Ethernet"
# Set-NetQosDcbx -Willing $True
# New-NetQosTrafficClass -Name "TC_EF" -Priority 5 -BandwidthPercentage 30 -Algorithm ETS
# Set-NetQosPolicy -Name "Game-RT-UDP" -PolicyStore Local -PriorityValue8021Action 5
rollback-qos.ps1
# 回滚本地 QoS 策略
$names = "Game-RT-UDP","Game-Voice-UDP","Game-Match-TCP","Game-Replay-Background"
foreach($n in $names){
$p = Get-NetQosPolicy -PolicyStore Local -ErrorAction SilentlyContinue | ? Name -eq $n
if($p){ Remove-NetQosPolicy -Name $n -PolicyStore Local -Confirm:$false; Write-Host "Removed: $n" }
}
你可以按这个表替换成自己的端口/策略
| 你的流量 | 端口/协议 | DSCP | 是否限速 | 备注 |
|---|---|---|---|---|
| 实时对战 | UDP X–Y | 46(EF) | 否 | 高 PPS,小包 |
| 实时语音 | UDP A–B | 34(AF41) | 否 | 抖动敏感 |
| 匹配/信令 | TCP C | 26(AF31) | 否 | 建连/控制 |
| 回放上传 | TCP D | 8(CS1) | 是(如 200Mb) | 大对象 |
| 日志/分析 | TCP E | 8(CS1) | 视需要 | 批量/可延迟 |
那晚我把脚本跑完,抓包里 ip.dsfield.dscp == 46 的 UDP 小包像“插队卡”一样稳稳当当地先出去。ToR 的 EF 队列没有再看到丢包尖刺,游戏 RTT 的 P99 从 118ms 落到 40ms 出头。最妙的是,观战回放照样传,只是速度变成了“礼貌型”的 200Mb。玩家没再骂,我们继续喝冰的。
后来我把这套改成了域控 GPO,下发到香港、东京和新加坡几组 Windows 服务器上。QoS 不是银弹,但在 IDC 内部和边界保住队列,这件事就足以撑住大多数的“夜半惊魂”。
如果你也在为实时对战的延迟发愁,照这篇把 DSCP 标记 + 背景限速 +(可选的)ETS 预留先做起来。等你把机房里那点看不见的秩序理顺了,玩家就能把注意力放回到该放的地方:游戏本身