网络游戏香港服务器如何在 Windows Server 环境中部署多台“匹配服务器”并实现负载均衡
技术教程 2025-09-20 09:57 196


凌晨两点,香港MEGA-i机房内,我蹲在42U机柜底层把最后一根 DAC 线插进25G 交换机,上层两台 Windows Server 2022 正在跑验证脚本,NLB 集群不停闪黄灯。第二天上午十点要开区,匹配服务要抗住首发洪峰。我不想靠“玄学”,只认具体指标、可复现的流程和能救火的应急脚本。

目标与约束

业务目标:在香港机房部署一组匹配服务器(match server),支撑万级并发,满足房间匹配/开服高峰,对外提供 TCP/UDP 入口。

环境约束:

  • 操作系统:Windows Server 2022 Datacenter(部分节点 2019 亦可)
  • 负载均衡:优先软件方案(Windows NLB、Nginx-Stream on Windows),必要时预留硬件 LB/云 LB 接入位。
  • 协议:HTTP/HTTPS(匹配 API)、TCP/UDP(实时对战/会话保持)。
  • 网络:香港本地 25G 接入,向大陆/东南亚有多线 BGP(PCCW/CMI/NTT),后端专网 10/25G。
  • 上线窗口:T+0 夜间变更,可随时回滚。

1. 架构总览(说在前面)

我们把“匹配”从“实时长连”里剥离:

  • 匹配 API(HTTP/gRPC):无状态,放在 HTTP 负载均衡后,做令牌校验、分配后端房间节点。
  • 房间/会话服务器(TCP/UDP):长连接与状态保持,不走七层,直连后端节点或走四层 LB。
  • 服务发现:Consul(Windows 版)记录每台房间节点的健康度/负载;匹配 API 按一致性哈希或最少连接挑选节点并把“直连地址”返给客户端。
  • 四层 LB只做入口聚合/灰度/应急(例如需要一个固定域名/端口给客户端),否则尽量直连减少抖动。
[Client] 
   |  HTTPS
   v
[Edge/WAF/HTTPS LB]  ---> [Match API (Windows Service, Kestrel)]  ---> [Consul/Redis]
                                                   | (select node)
                                                   v
                                     [Room Server List (Windows)] <--- Health checks
                                                   |
                            TCP/UDP Direct or via L4 LB (Nginx-Stream/NLB)
                                                   v
                                        [Room Server (Windows)]

2. 硬件与网络选型(我真装过的那几台)

角色 型号/形态 CPU 内存 系统盘 数据盘 网卡 OS
Match API 节点 ×2 Dell R650 Xeon Gold 6430 ×1 (32c) 128 GB 2×480G SATA 2×3.84TB NVMe 2×25G SFP28 WS 2022
Room 节点 ×6 Dell R650 / HPE DL360 同上或 24c 起 128–256 GB 2×480G 2×3.84TB NVMe 2×25G WS 2022
LB 节点 ×2 同上 16–24c 64–128 GB 2×480G - 2×25G WS 2022
存储/监控 机房共享或独立盒子 - - - - 10/25G -

交换机:2× Top-of-Rack 25G(IRF/MLAG),前端公网 VLAN,后端管理/存储 VLAN 分离。

IP 规划(示例):

网络 用途 网段 备注
VLAN 100 公网/对外入口 103.x.x.0/24 LB/入口 VIP
VLAN 200 后端业务 10.10.20.0/24 API ⇄ Room
VLAN 210 管理 10.10.21.0/24 RDP/监控/备份

3. 系统级优化(上机先做不走弯路)

3.1 BIOS/电源/频率

  • BIOS 里关掉深度 C-State,开高性能/性能偏好,固定全核睿频。
  • NUMA 拥抱:让房间进程绑核(后文有示例)。

3.2 Windows 网络栈

# 高性能电源
powercfg -setactive SCHEME_MIN

# RSS/RSC/Offload
netsh int tcp set global rss=enabled autotuninglevel=normal
netsh int tcp set global rsc=enabled
# 某些 NIC 需要手工关/开 LSO 和中断调制,建议在驱动属性里按厂商白皮书设置

# 提升连接队列
reg add "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v TcpNumConnections /t REG_DWORD /d 0x00fffffe /f

# 防火墙快速规则(示例:TCP 40000-40100,UDP 30000-30100)
New-NetFirewallRule -DisplayName "Room-TCP" -Direction Inbound -Protocol TCP -LocalPort 40000-40100 -Action Allow
New-NetFirewallRule -DisplayName "Room-UDP" -Direction Inbound -Protocol UDP -LocalPort 30000-30100 -Action Allow

3.3 NIC Teaming / VLAN

单机双口 25G:Switch Independent / Dynamic 模式(LBFO)或使用 SET(配合 Hyper-V)。

前端与后端 VLAN 分离,后端可开 MTU 9000(仅在交换机/端到端都支持时)。

4. 基础件安装(可打“金镜像”)

# .NET 8 Hosting Bundle(跑 Kestrel 的服务)
# (离线包放在 \\repo\pkgs\dotnet-hosting-8.0.x-win.exe)
Start-Process -FilePath "\\repo\pkgs\dotnet-hosting-8.0.x-win.exe" -ArgumentList "/quiet" -Wait

# IIS 可选(只当你需要)
Install-WindowsFeature Web-Server, Web-WebSockets, Web-Http-Logging

# Nginx for Windows(作为四层LB)
# 解压到 C:\nginx,使用 NSSM 注册为服务
nssm install nginx "C:\nginx\nginx.exe"
nssm set nginx AppDirectory "C:\nginx"
nssm start nginx

# Consul (Windows) - 服务发现
New-Item -ItemType Directory C:\consul
Copy-Item \\repo\pkgs\consul.exe C:\consul\
nssm install consul "C:\consul\consul.exe" agent -server -bootstrap-expect=1 -data-dir="C:\consul\data" -bind=10.10.20.10 -client=0.0.0.0
nssm start consul

5. 服务形态与发布

5.1 Match API(.NET 8 Windows 服务)

只做鉴权 + 选址:根据玩家 playerId/region/mmr 选择一个健康的房间节点,把IP:Port返给客户端。

一致性哈希示意(C# 伪码):

var nodes = consul.GetHealthy("room"); // 带当前连接数/CPU/延迟
var node = ConsistentHash.Select(playerId, nodes); // 或最小连接 + 亲和
return new { endpoint = $"{node.ip}:{node.port}", token = Sign(node, playerId) };

5.2 Room Server(Windows Service)

每台监听 TCP:40000-40100、UDP:30000-30100(按服拆分)。

启动参数包含 --affinity "0-15" 等,结合 start /affinity 或 Job Object 绑定 NUMA。

6. 负载均衡方案对比与选择

方案 层次 协议 优点 缺点 适用
Windows NLB L4 TCP/UDP 原生、简单、共享VIP 对交换机有要求(单播/多播),大流量抖动可能性,功能弱 小规模直进产线/应急
Nginx-Stream on Windows L4 TCP/UDP 配置灵活、灰度/限流易做 Windows 版本生态略弱,健康检查主要被动 中等规模、需要灵活路由
硬件/商用 LB(F5/Kemp/NGINX Plus) L4/L7 TCP/UDP/HTTP 强大的健康检查、统计、稳定 成本高 核心外网入口/大规模

我的做法:外网入口用商用或云 LB(可选),内网/游戏端口用 Nginx-Stream,NLB 作为兜底或简单场景。

7. 实施 A:Windows NLB(四层 VIP)

这一步是我在凌晨先通的“兜底通路”,方便立刻有 VIP 给客户端验证与联调。

7.1 安装与建群集

Install-WindowsFeature NLB -IncludeManagementTools

# 在 LB 节点上创建 NLB 群集(建议单播模式 + 独立前端 NIC)
New-NlbCluster -InterfaceName "Ethernet1" -ClusterPrimaryIP 103.x.x.10 -OperationMode Unicast

# 添加节点
Add-NlbClusterNode -NewNodeName "lb-02" -InterfaceName "Ethernet1" -HostName "lb-02" -ClusterName "103.x.x.10"

# 端口规则(TCP 40000-40100, UDP 30000-30100)
# NLB 用端口规则把 VIP 流量分散到两个 LB 节点,再由 LB 节点转发到后端(或直接在 LB 节点本机跑 Nginx-Stream)
Add-NlbClusterPortRule -ClusterName "103.x.x.10" -StartPort 40000 -EndPort 40100 -Protocol TCP -Affinity Single
Add-NlbClusterPortRule -ClusterName "103.x.x.10" -StartPort 30000 -EndPort 30100 -Protocol UDP -Affinity None

坑 1:交换机 ARP/单播模式

单播模式下,NLB 会让交换机看不到真实 MAC ↔ IP 的映射,容易泛洪。

解决:前端 NIC 独立,与后端分 VLAN;必要时让机房静态 ARP 或改 多播 + IGMP(机房得开 IGMP Snooping)。

坑 2:NLB 与 NIC Teaming 冲突

某些驱动/固件版本会导致 NLB 漂移/掉包。我的实践:前端口不做 Team,后端口可 Team。

8. 实施 B:Nginx-Stream on Windows(四层路由/灰度)

C:\nginx\conf\nginx.conf(简化版,用被动健康检查)

worker_processes auto;
events { worker_connections  65535; }

stream {
    # TCP (房间)
    upstream room_tcp_backend {
        least_conn;
        server 10.10.20.31:40000 max_fails=3 fail_timeout=10s;
        server 10.10.20.32:40000 max_fails=3 fail_timeout=10s;
        server 10.10.20.33:40000 max_fails=3 fail_timeout=10s;
    }
    server {
        listen 0.0.0.0:40000;
        proxy_connect_timeout 1s;
        proxy_timeout 30m;           # 长连
        proxy_pass room_tcp_backend;
    }

    # UDP (房间)
    upstream room_udp_backend {
        hash $remote_addr consistent; # UDP 粗粒度亲和
        server 10.10.20.31:30000 max_fails=3 fail_timeout=10s;
        server 10.10.20.32:30000 max_fails=3 fail_timeout=10s;
        server 10.10.20.33:30000 max_fails=3 fail_timeout=10s;
    }
    server {
        listen 0.0.0.0:30000 udp;
        proxy_responses 0;           # 不期望响应限制
        proxy_timeout 5m;
        proxy_pass room_udp_backend;
    }
}

要点:

TCP 用 least_conn 保持均衡;UDP 用 hash $remote_addr consistent 粗粘性(UDP 无连接,尽量稳定)。

主动健康检查若需要,考虑商用 NGINX Plus 或在 Match API 侧摘除不健康节点(动态生成 upstream 列表并 nginx -s reload)。

灰度发布(把新节点加权 10%):

upstream room_tcp_backend {
    least_conn;
    server 10.10.20.31:40000 weight=9;
    server 10.10.20.99:40000 weight=1; # 新版
}

9. 服务发现与健康度(Consul on Windows)

注册示例 C:\consul\conf\room-31.json:

{
  "service": {
    "name": "room",
    "id": "room-31-30000",
    "address": "10.10.20.31",
    "port": 30000,
    "check": {
      "name": "udp-port-open",
      "shell": "powershell -Command \"& {Test-NetConnection -ComputerName 10.10.20.31 -Port 30000 -InformationLevel Quiet}\"",
      "interval": "10s",
      "timeout": "2s"
    },
    "meta": {
      "cpu": "24",
      "region": "hk",
      "maxRooms": "2000"
    }
  }
}

注意:UDP 健康探测可用自定义探针脚本(发送特定握手包),上例仅演示端口可达。

Match API 选址策略:

  • 优先同城/低 RTT;
  • 过滤 check == passing;
  • least_conn 或 consistent_hash(playerId);
  • 返回直连地址 + 短期授权 token(避免被外部滥用直连端口)。

10. 监控与容量估算(可复现实数)

10.1 核心指标

类别 指标 目标/告警线
系统 CPU<65%、RunQueue<1×核数 10m 中位 + P95
网络 单口带宽<70%、丢包<0.1% ethtool/交换机
套接字 TCP Established、UDP pps P95 < 峰值 80%
应用 匹配成功率、房间创建耗时 <300ms P95
LB 连接数/失败率/后端状态 后端探针失败<1%

PerfMon 计数器建议:

  • \Processor(_Total)\% Processor Time
  • \TCPv4\Connections Established
  • \UDPv4\Datagrams Received/sec
  • \Process(room.exe)\Handle Count / Working Set
  • \Network Interface(*)\Packets Outbound Errors

10.2 粗略容量估算(示例)

  • 每房间 8 人,平均会话 30 分钟。
  • 目标同时在线(CCU) 20,000 人。
  • 房间数 ≈ 20,000 ÷ 8 = 2,500 间。
  • 单节点承载 400 间(留 20% 余量),需房间节点 ≈ 2,500 ÷ 400 = 6.25 → 7 台。
  • 我们先上线 6 台 + 1 台热备(与实际清单一致)。

我上线夜测时,用 PsPing/自研压测把 TCP QPS 顶到 25k/s,UDP pps 顶到 200k/s,25G 口只上去 6–8 Gbps,CPU 40–55%,余量够开服。

11. 压测与联调

11.1 工具

  • PsPing(Sysinternals):TCP/UDP 延迟与吞吐。
  • Bombardier(HTTP 压测)或 wrk(跑在旁路 Linux/WSL)。
  • 自研脚本:模拟玩家登录 → 请求匹配 → 直连房间 → 维持 30 分钟。

11.2 快速命令

# TCP 连接延迟
.\psping.exe -n 1000 103.x.x.10:40000

# UDP 吞吐(发送端)
.\psping.exe -u -l 1200 -n 100000 103.x.x.10:30000

12. 线上故障与坑(当晚真实踩过)

NLB 单播导致交换机广播:同 VLAN 其他机器被“拖慢”。

解决:前端 NIC 独立到 VLAN100,后端业务在 VLAN200,NLB 只在前端;向机房申请 静态 ARP 或改 多播+IGMP。

UDP 粘性不足导致状态丢:Nginx 对 UDP 做 $remote_addr 哈希,公网 NAT 情况下同一出口会让很多玩家被哈到同一后端。

解决:把“真正的亲和”放在 Match API 选址,让客户端拿到直连地址,避免四层 LB 做粘性。

驱动中断调制过强:低延迟抖动大。

解决:把 NIC 的 Interrupt Moderation Rate 调低,打开 RSS 并根据 CPU 核数设置 RSS Queues=8/16。

Windows 更新重启:夜里被策略推更新。

解决:统一 gpedit.msc 配置维护时窗,维护期外禁止自动重启;生产批更新全部走 WSUS/离线。

日志写入打爆系统盘:Kestrel 和应用日志默认 C 盘。

解决:C:\logs 映射到 NVMe,按天滚动 + 压缩 + Filebeat 出口。

13. 回滚与应急剧本(我贴墙上的那张纸)

策略 A:摘除新节点(Consul 标记 maintenance → Match API 不再派号);

策略 B:Nginx 热重载

nginx -t && nginx -s reload

策略 C:切回 NLB 直连(在 DNS 将 game.domain.com 指到 NLB VIP,仅保留最稳定的端口段)。

策略 D:限流/排队(Match API 侧返回“排队位”与重试时间,防止雪崩)。

策略 E:熔断开关:一键把“直连模式”替换成“全走 LB”,反之亦然。

14. 运维脚本片段(能在凌晨救你的那种)

批量开端口 + 性能开关

$tcp=40000..40100; $udp=30000..30100
$tcp | % { New-NetFirewallRule -DisplayName "Room-TCP-$_" -Direction Inbound -Protocol TCP -LocalPort $_ -Action Allow }
$udp | % { New-NetFirewallRule -DisplayName "Room-UDP-$_" -Direction Inbound -Protocol UDP -LocalPort $_ -Action Allow }

netsh int tcp set global rss=enabled
netsh int tcp set global rsc=enabled

发布 Room 服务(NSSM)

nssm install room "C:\apps\room\room.exe" --port-tcp 40000 --port-udp 30000 --affinity "0-15"
nssm set room AppDirectory "C:\apps\room"
nssm set room Start SERVICE_AUTO_START
nssm start room

Match API 拉取健康后端(PowerShell 伪代码)

$healthy = (Invoke-RestMethod http://127.0.0.1:8500/v1/health/service/room) `
           | Where-Object {$_.Checks.Status -eq "passing"} `
           | ForEach-Object { $_.Service.Address + ":" + $_.Service.Port }

# 根据负载择优(示例:随机/最少连接由应用内实现)
$node = Get-BestNode $healthy

15. 安全加固(别忽视)

  • 最小权限:服务账号非 admin,目录 ACL 最小化;
  • TLS:外网入口强制 TLS1.2+,HTTP/2 开启;
  • Anti-DDoS:机房与云清洗线路接入位预留;
  • IP 白名单:运维/RDP 全走堡垒机与管理 VLAN;
  • 审计:Windows 事件订阅 + 中央日志(ELK/CloudWatch 等)。
  • 16. 成本与延迟(有个大致心里数就不慌)
  • 节点数:6(房间)+2(API)+2(LB)= 10 台;
  • 机柜功耗:单台 350–450W,满载 500W;10 台 ≈ 5 kW(含交换机+冗余)→ 半柜足够;
  • 带宽:外网峰值 5–8 Gbps,后端专网 10–15 Gbps;
  • 时延:香港本地 1–3 ms,新加坡 35–45 ms,华南 20–35 ms(实际以你的运营商路由为准)。

17. 验收清单(上线前逐条打钩)

  1.  NLB VIP 可达,端口规则生效,交换机 IGMP/单播配置确认
  2.  Nginx-Stream 配置 nginx -t 通过且可热重载
  3.  Match API 返回直连地址与 token,客户端直连成功率 > 99%
  4.  Consul/监控全绿,核心指标 P95 在阈值内
  5.  灰度/回滚开关已演练一次
  6.  日志分盘、备份与保留策略启用

18. 清晨的机房与第一波玩家

天亮前,我在机柜门上贴好了“变更结束”的标签,NLB 的心跳终于稳定成绿色。第一波玩家涌入,Match API 的曲线先抬头又平缓,Nginx 的连接数像海浪一样起伏,房间分配的失败率停在 0.2%。我把手里那杯彻底凉掉的美式一口喝完,给远程的策划发了条消息:“可以开区了。”

机房外的电梯叮的一声,我知道这套在 Windows Server 上打磨出来的匹配与负载方案,今晚又多了几条能救火的命令、几个能复用的脚本,和一段值得写进手册的故事。

附:小抄(速查表)

常用端口

  • Match API(HTTPS):443/8443
  • Room Server:TCP 40000–40100、UDP 30000–30100
  • Consul:8500(HTTP API)

一键重载

nginx -t && nginx -s reload

NLB 查看状态

Get-NlbClusterNode | ft HostName, StatusCode, Draining

快速健康探测(TCP)

Test-NetConnection 103.x.x.10 -Port 40000