香港服务器使用Windows Server 2019时,如何监控CN2/GIA线路带宽利用率并优化流量调度?

凌晨两点,我在香港MEGA-i机房把最后一条万兆光纤插回 X710 的 SFP+,看了一眼屏幕上滚动的带宽曲线:CN2/GIA 的通道又被挤到 90% 了。运营同学在群里说,北上广用户开始反馈‘打卡慢’。我深吸一口气,打开了那台 Windows Server 2019——今晚,得把监控和调度彻底拉到位。”
目标与难点
- 目标 1: 精确监控 CN2/GIA 线路带宽利用率(含 95 分位、突发峰值、单流与并发)。
- 目标 2: 当 CN2/GIA 接近拥塞时,对入口和出口流量做可回滚的调度与分流,确保大陆用户体验不崩。
难点:
- Windows 本机双上联(CN2/GIA & 普通 BGP)下,怎么测得准、怎么切得稳。
- 只在应用层/操作系统层(尽量不依赖昂贵硬件)做可观测 + 可编排。
现场环境与参数
| 模块 | 规格/型号 | 关键参数 |
|---|---|---|
| 机房 | 香港 MEGA-i(示意) | ToR 万兆交换,交付 2 条上联 |
| 服务器 | Dell R740xd(示例) | 2×Xeon Silver、128GB RAM、2×Intel X710-DA2(10G SFP+) |
| 系统 | Windows Server 2019 Datacenter | 启用 Hyper-V(可选) |
| 网络上联 A | CN2/GIA 专线 | 物理 10G,计费承诺 200 Mbps(95th),公网 /30 |
| 网络上联 B | 普通 BGP | 物理 10G,计费承诺 1 Gbps,公网 /30 |
| 公网 IP | A/B 各一组 | 作为双入口(两组 A 记录) |
| 应用 | Web API + 文件分发 | 峰值并发较高、包大小分布极不均衡 |
关键设计:入口用 DNS 级流量调度(按地域/权重),出口用静态路由+前缀表(CN 段走 A;其余走 B),观测与编排全部在 Windows 上完成。
总体架构(文字示意)
观测面(Windows 上)
- Telegraf(Windows 版)抓取本机 NIC 计数器 + SNMP 轮询边缘设备端口(如对接运营商的交换/路由口)。
- 数据写入 InfluxDB(可自建/云端),Grafana 出图。
- PowerShell 守护脚本做阈值判定与调度动作编排(改 DNS 权重 / 改路由 / 下发 API)。
入口调度
- 两个 A 记录:cn.g.example.com(指向 CN2/GIA-IP)、intl.g.example.com(指向 BGP-IP)。
- 业务域名 api.example.com 使用负载均衡型 DNS(支持权重/地理/延迟),Windows 脚本通过 API 动态改权重(例如 Cloudflare/NS1/阿里 GTM 均可,本文示例 Cloudflare)。
出口调度
- Windows 的静态路由:导入「中国大陆 IP 段聚合表」→ 下一跳为 CN2/GIA 网关;默认路由→ BGP 网关。
- 大流量非 CN 目的地(国际下载/第三方云)自然走 BGP,避免挤占 CN2/GIA。
为什么这样做?
- 入口侧,DNS 权重是对客户端“选线”的最温和手段,无状态、可快速回滚。
- 出口侧,Windows 虽不支持复杂 PBR,但按目的前缀静态路由已足够把 CN/INTL 分开。
- 所有决策“看得见”:曲线是证据,动作可审计。
一、把“能看到”做扎实:CN2/GIA 带宽监控
1)Windows 侧计数器(Get-Counter)
# 每秒采样一次,抓两块物理口(注意按实际名称/索引替换)
$ifCN2 = "Intel[R] Ethernet 10G 2" # CN2/GIA
$ifBGP = "Intel[R] Ethernet 10G 1" # 普通 BGP
$paths = @(
"\Network Interface($ifCN2)\Bytes Total/sec",
"\Network Interface($ifBGP)\Bytes Total/sec",
"\TCPv4\Connections Established",
"\Processor(_Total)\% Processor Time"
)
Get-Counter -Counter $paths -SampleInterval 1 -MaxSamples 0 |
ForEach-Object {
# 采样流,管道到自定义处理/落盘(略)
}
坑点:Windows 更新或驱动升级后,接口实例名会变化(甚至多出 “#2”)。建议改为 Interface GUID 绑定(用 Get-NetAdapter 拿到 InterfaceGuid 并映射到 Performance Counter)。
2)Telegraf(Windows)+ InfluxDB:持久化与 95th 统计
安装与服务化(示例)
# 下载略,假设已解压到 C:\telegraf
New-Service -Name telegraf -BinaryPathName "C:\telegraf\telegraf.exe --service start" -DisplayName "Telegraf" -StartupType Automatic
Start-Service telegraf
核心 telegraf.conf 片段(Windows + SNMP)
# 输出到 InfluxDB
[[outputs.influxdb]]
urls = ["http://influxdb.example.com:8086"]
database = "hk-edge"
username = "telegraf"
password = "********"
# 采 Windows NIC 性能计数器
[[inputs.win_perf_counters]]
[[inputs.win_perf_counters.object]]
ObjectName = "Network Interface"
Instances = ["Intel[R] Ethernet 10G 2","Intel[R] Ethernet 10G 1"]
Counters = ["Bytes Received/sec","Bytes Sent/sec","Packets/sec","Packets Outbound Errors","Packets Received Errors"]
Measurement = "win_netif"
IncludeTotal = false
[[inputs.win_perf_counters.object]]
ObjectName = "TCPv4"
Instances = ["_Total"]
Counters = ["Connections Established","Segments/sec"]
Measurement = "win_tcp"
# 采边缘设备(如运营商交付交换/路由端口)的 SNMP 计数器
[[inputs.snmp]]
agents = ["udp://203.0.113.2:161"] # 运营商交付口对端设备(若允许)
version = 2
community = "public"
name = "edge_snmp"
[[inputs.snmp.table]]
name = "ifXTable"
oid = "IF-MIB::ifXTable"
[[inputs.snmp.table.field]]
name = "ifName"
oid = "IF-MIB::ifName"
[[inputs.snmp.table.field]]
name = "ifHCInOctets"
oid = "IF-MIB::ifHCInOctets"
[[inputs.snmp.table.field]]
name = "ifHCOutOctets"
oid = "IF-MIB::ifHCOutOctets"
坑点:SNMP 若只能拿到 32-bit 计数器,在万兆口下会很快回卷导致速率成负值;务必启用 ifHCIn/OutOctets(64-bit)。
Grafana 侧我们重点做两条曲线:
CN2/GIA 5s 平滑速率、BGP 5s 平滑速率
CN2/GIA 95th(Rolling 24h) 与 承诺带宽 的对比线(≥85% 告警)
二、压测与标定
上行/下行 iperf3(Windows 可用 Chocolatey 安装)
choco install iperf3 -y
# 向内地测(确认 CN2/GIA 方向容量 & 单流/多流差异)
iperf3.exe -c cn-probe.example.cn -t 30 -P 8
# 向海外测(确认 BGP 方向)
iperf3.exe -c sg-probe.example.net -t 30 -P 8
经验:CN2/GIA 单流可能偏保守,多流(-P)能更接近链路上限;标定时要记录 并发曲线,否则上线后会误判“带宽被吃满”。
三、入口调度:权重随带宽而动(以 Cloudflare API 为例)
思路:api.example.com 背后两个 Origin(CN2-IP / BGP-IP),Windows 上的 PowerShell 根据监控阈值增减权重。CN2>85% 持续 60s → 降低其权重,把一部分新请求引向 BGP;恢复 <70% 持续 3min → 提权回归。
最小可用 PowerShell 脚本(片段)
# 参数
$zoneId = "xxxxxxxxxxxxxxxxxxxx"
$lbId = "yyyyyyyyyyyyyyyyyyyy"
$token = "CF_API_TOKEN"
function Set-PoolWeight {
param([string]$poolId, [int]$weight)
$uri = "https://api.cloudflare.com/client/v4/user/load_balancers/pools/$poolId"
$body = @{ weight = $weight } | ConvertTo-Json
Invoke-RestMethod -Method PATCH -Uri $uri -Headers @{ "Authorization"="Bearer $token" } -ContentType "application/json" -Body $body
}
function Get-CN2Utilization {
# 从 Influx 取最近 60s 平均带宽(或直接用 Get-Counter 实时平均)
# 这里用假值演示
return 0.88 # 88%
}
$cn2PoolId = "pool_cn2"
$bgpPoolId = "pool_bgp"
while ($true) {
$u = Get-CN2Utilization
if ($u -ge 0.85) {
Set-PoolWeight -poolId $cn2PoolId -weight 30
Set-PoolWeight -poolId $bgpPoolId -weight 70
} elseif ($u -le 0.70) {
Set-PoolWeight -poolId $cn2PoolId -weight 60
Set-PoolWeight -poolId $bgpPoolId -weight 40
}
Start-Sleep -Seconds 15
}
实战提醒
- DNS TTL 不要过低(建议 30–60s),过低会增加权威 DNS 压力且部分递归解析器未严格遵循;过高又让“刹车变钝”。
- 若使用“会话亲和”,权重变化对存量连接不生效,要预估“换道延迟”。
- 部分大陆网络的 DNS 缓存粘滞时间远超 TTL,观察曲线再决定收敛策略(一次降 10% vs 30%)。
四、出口调度:Windows 静态路由按“目的地”分流
原则:
CN 段 → 走 CN2/GIA 的默认网关(例如 203.0.113.9)。
其它 → 走 BGP 网关(例如 198.51.100.9)。
1)设置网关与度量(Metric)
# 为两块上联 NIC 设置网关与度量
Get-NetIPInterface | ft ifIndex,InterfaceAlias,AddressFamily,InterfaceMetric
# 假设 CN2 接口度量设为 10,BGP 接口度量设为 50
Set-NetIPInterface -InterfaceAlias "CN2-X710" -InterfaceMetric 10
Set-NetIPInterface -InterfaceAlias "BGP-X710" -InterfaceMetric 50
这一步只是“默认路径偏好”,真正把 CN 目的地下沉到 CN2 还需要导入前缀。
2)导入中国大陆聚合前缀(示例)
生产建议用日更的聚合表(CN CIDR 聚合),本文演示少量前缀写法。
$cnGw = "203.0.113.9" # CN2/GIA 网关
# 北京、电信华南等示例(请替换为实际聚合表)
$cnPrefixes = @(
"36.0.0.0/8",
"58.0.0.0/7",
"101.0.0.0/8",
"103.0.0.0/8",
"106.0.0.0/8",
"110.0.0.0/8",
"111.0.0.0/8",
"112.0.0.0/5"
)
foreach ($p in $cnPrefixes) {
route.exe -p add $p mask 255.0.0.0 $cnGw IF <CN2_IFINDEX>
}
坑点:
- Windows route add 对掩码写法敏感,按前缀转换为掩码;批量导入要校验 IFIndex。
- 聚合过度会“漏网”;聚合不足会路由表过大。建议在 Windows 上只保留 /8~ /12 的上层聚合,更细的由虚拟路由器承接(见下一节“进阶方案”)。
五、进阶:在 Windows 上“挂一个”虚拟路由器做更细粒度策略
当需要:
- 对ASN / Geo / 应用端口做更细分流;
- 需要ECMP/健康探测;
- 未来考虑BGP对接……
做法:启用 Hyper-V,起一台 VyOS 或 FRR on Ubuntu 的虚拟路由器(两上联各一条 vNIC,内侧一条 vNIC 接入 Windows)。Windows 的默认网关指向这台虚机;所有策略路由放在虚机上完成。
VyOS 极简示例(仅演示思路)
set interfaces ethernet eth0 address '203.0.113.10/30' # CN2 上联
set interfaces ethernet eth1 address '198.51.100.10/30' # BGP 上联
set interfaces ethernet eth2 address '10.0.0.1/24' # 内网指向 Windows
# 默认走 BGP
set protocols static route 0.0.0.0/0 next-hop 198.51.100.9
# 中国大陆前缀走 CN2(可用脚本每日导入更细粒度表)
set protocols static table 100 route 36.0.0.0/8 next-hop 203.0.113.9
# ... 批量导入若干
set policy route PBR-CN rule 10 set table 100
set policy route PBR-CN rule 10 destination group network-group CN-Prefix
set interfaces ethernet eth2 policy route 'PBR-CN'
commit; save
优点:策略灵活、回滚快、对 Windows 透明;缺点:多一层运维(但值得)。
六、联动编排:让“监控”驱动“动作”
阈值策略(可按需调整):
- 告警线:CN2/GIA 1 分钟平均 ≥ 85% 承诺带宽。
- 限流线:CN2/GIA 5 分钟 95th ≥ 90% 承诺带宽且“连接建立速率”上升 → 判定突发。
- 恢复线:连续 3 分钟 ≤ 70%。
动作优先级:
- 入口权重微调(-10%/-20% 级别)。
- 出口非关键目的地强制走 BGP(增补静态前缀或在虚路由器加 rule)。
- 应用层下载分片/并发数调低(可选:IIS/反代层限速)。
PowerShell 守护脚本(示例骨架)
$commitCn2 = 200 * 1000 * 1000 # 200 Mbps
$alarm = $commitCn2 * 0.85
$recover = $commitCn2 * 0.70
function Current-CN2-Bps { # 返回最近60秒均值
# 可直接从 Influx 取,也可本地滑窗;此处省略实现
}
while ($true) {
$bps = Current-CN2-Bps
if ($bps -ge $alarm) {
# 1) 降入口 CN2 权重
# 2) 加出口前缀(把若干下载节点/对象存储前缀切到 BGP)
# 3) 记录操作审计
} elseif ($bps -le $recover) {
# 恢复入口权重,移除临时前缀
}
Start-Sleep 15
}
七、线上“坑”与我的止损手法
NIC 统计错口:X710 开/关 SR-IOV、VMQ 后,计数器换实例名。
做法:统一用 InterfaceGuid + Get-NetAdapterStatistics,并在 Telegraf 里配置 IncludeTotal=false。
计时漂移:Windows 时间飘了几百毫秒,Influx 写入顺序乱。
做法:启用 NTP + PTP(能开就开),Grafana 图上做 5s 平滑。
Jumbo Frame“黑洞”:A 口 MTU 9000,B 口 1500,应用回源跨口时包碎片/丢弃。
做法:除非链路端到端确认一致,否则统一 1500,万不得已再开巨帧。
DNS 切流“不灵”:某些递归 DNS 粘 TTL。
做法:逐步调整权重 + 看 10 分钟窗口内“新建连接速率”是否跟着变化;别指望秒切。
SNMP 被墙:对端设备 SNMP 只对运维网开放。
做法:让运营商在交付口开只读并限源;实在不行,以 Windows NIC 为主,SNMP 为辅。
CPU 突刺:开启过多实时计算。
做法:把统计放到 Influx/Grafana,Windows 侧仅做轻量阈值判断。
八、结果复盘(上线一周)
| 指标 | 上线前 | 上线后 |
|---|---|---|
| CN2/GIA 95th(24h) | 92% of commit | 74% of commit |
| 峰值拥塞持续时长(分钟/日) | 45 | 8 |
| 海外用户 95th 响应(P95) | 820 ms | 610 ms |
| 大陆用户 95th 响应(P95) | 580 ms | 520 ms |
| 接口丢包(万分比) | 2.3 ‰ | 0.6 ‰ |
最大的收益并不是“跑更快”,而是当 CN2/GIA 真要被打满时,能有序退让,让关键请求先过去、把非关键的先让到 BGP 去。
附:关键命令/配置速查
Windows NIC 调优(示例)
# 关闭老网卡上的 Flow Control(避免卡死)
Get-NetAdapterAdvancedProperty -Name "CN2-X710" -DisplayName "Flow Control" | Set-NetAdapterAdvancedProperty -DisplayValue "Disabled"
# 打开 RSS;必要时调整队列数
Enable-NetAdapterRss -Name "CN2-X710","BGP-X710"
Set-NetAdapterRss -Name "CN2-X710" -NumberOfReceiveQueues 8
IIS/反代限速(应用层兜底)
对大文件下载站点开启每连接限速、限制并发,避免瞬时“挤穿”CN2。
Influx 95th 计算(Flux 示意)
from(bucket: "hk-edge")
|> range(start: -24h)
|> filter(fn: (r) => r._measurement == "win_netif" and r.ifName == "CN2-X710" and r._field == "bps_total")
|> aggregateWindow(every: 1m, fn: mean)
|> percentile(column: "_value", percentile: 95.0)
可选方案:只在 Windows 做,不上虚拟路由器?
可以,但复杂度会上到路由表维护(前缀多、变更频繁),且难以做多条件策略。规模小时可行,流量大了建议上 VyOS/FRR 虚机,Windows 只做观测与编排。
结尾:把“凌晨两点”的紧张,换成“白天八点”的可预期
“当我把最后一次权重调回去,CN2/GIA 的曲线稳稳落在 70% 附近。工位上的咖啡已经凉了,但心是热的:从这晚开始,我们不是被流量牵着跑,而是用数据牵着流量走。
后来新同事问我,为什么偏偏在 Windows 上把这套做起来?我笑了笑:因为我们就跑在这台机器上,这才叫落地。”
TL;DR(拿去即用)
- 监控:Telegraf(Windows)采 NIC + SNMP → Influx → Grafana;盯 5s 平滑、95th、连接建立速率。
- 入口调度:DNS 权重随阈值自动调整(PowerShell 调用 DNS/LB API),TTL 30–60s,渐进式。
- 出口调度:Windows 静态前缀(CN → CN2,默认 → BGP);流量复杂时上 VyOS/FRR 虚机做 PBR。
- 编排:PowerShell 守护脚本把“曲线”变成“动作”,动作可审计、可回滚。
- 踩坑清单:接口名漂移、计数器回卷、时间漂移、巨帧黑洞、DNS 粘 TTL、SNMP 限源、CPU 突刺——文中都有止损手法。