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

如何在香港服务器的 Windows 系统中优化 IIS“长连接”,避免跨境电商高峰期交易失败

发布人:Minchunlin 发布时间:2025-09-04 08:32 阅读量:773


那天是黑色星期五的0:07,我一个人在香港荃湾机房的冷气里抱着纸杯咖啡,监控墙上红点狂闪:502/504 间歇飙升、支付回调丢失、购物车提交“卡转圈”。网络延迟倒不离谱(深圳/上海到香港 40–80ms 波动),可前端日志里写满了 backend_connection_closed。经验告诉我:连接并没有“坏”,而是“断得太快”或者“排不上队”。

这一晚,我把整条链路从 浏览器→CDN/LB→香港边缘 IIS(ARR 反代)→后端应用/支付网关 逐段掰开:Keep-Alive(长连接)策略、站点连接超时、内核队列、应用池队列、TIME_WAIT/端口耗尽、ARR 到后端的连接复用……它们任何一个“保守默认”,都可能在峰值时把交易压垮。

下面是我当晚到次日复盘的完整操作清单与参数基线,既适合快速落地,也能深入到内核 http.sys 与 TCP 栈。所有步骤都可以单独执行;你可以按需择要,也可以一口气把基线打齐。

现场环境与目标

  • 机房/地域:香港(到内地多段链路,RTT 40–80ms)
  • 前端:CDN + LB(七层;有空闲超时 Idle Timeout)
  • 边缘:Windows Server 2019/2022 + IIS 10,部分节点装 ARR 3.0 做反代/回源
  • 后端:.NET Core 应用(IIS In-Process 托管)+ 第三方支付网关(出站 HTTPS)
  • 硬件样例:2×EPYC/Gold、128–256GB RAM、NVMe SSD、双 10GbE(RSS 开启)
  • 目标:在峰值(>20k RPS,2–3 万并发连接)避免 5xx 与订单失败,保证支付/回调链路不断连、不卡队。

1) 打开并校准 IIS 长连接(Keep-Alive)与站点连接超时

1.1 启用 Keep-Alive

IIS 的 Keep-Alive 默认是开的,但我永远显式打开并纳管到配置里,防止误关或模板覆盖:

%windir%\system32\inetsrv\appcmd.exe set config "Default Web Site" ^
  -section:system.webServer/httpProtocol /allowKeepAlive:"True"

这个设置对应 <httpProtocol allowKeepAlive="true" />,官方文档明确说明其作用与默认值。
Microsoft Learn

1.2 调整“空闲连接超时”= Keep-Alive 实际闲置时长

IIS 没有单独名为 keepAliveTimeout 的键,Keep-Alive 的闲置时长由站点的 connectionTimeout 控。默认 2 分钟,我会根据上游 LB/CDN 的 Idle Timeout 和业务长轮询/WebSocket 情况做统一:例如把站点设置为 00:01:30(90s),并确保上游 Idle Timeout ≥ 90s,避免“上游先掐掉、IIS 还想留”的半途 RST。

:: 对单站点:
appcmd set config -section:system.applicationHost/sites ^
  "/[name='Default Web Site'].limits.connectionTimeout:00:01:30" /commit:apphost

:: 也可设默认值(所有新站点继承):
appcmd set config -section:system.applicationHost/sites ^
  /siteDefaults.limits.connectionTimeout:"00:01:30" /commit:apphost

connectionTimeout 默认 00:02:00;可按站点或默认层设置。

官方也提供 appcmd 示例。

经验值:支付页/结算页的长连接收益极大(TLS 复用、头部复用);但SSE/长轮询建议拉高到 120–180s,并保证上游 LB/CDN 的 Idle Timeout 不更短(很多云 LB 默认 60s/75s)。

2) 合理的连接上限与队列:别让请求“挤爆门口”

2.1 不轻易限制站点

通常保持“不设上限”(4294967295),不要在站点层堵门。确需限速/限连,请用 WAF/CDN 或网关。<site>.limits 同一处可配置。

2.2 扩大应用池队列长度(QueueLength)

IIS 入口队列在 http.sys(内核)→应用池队列 两段。应用池 QueueLength 默认 1000(不同资料/工具展示 1000 或 4000,视版本/工具而定),峰值下很容易排满导致 503/QueueFull。我统一把 关键站点的 AppPool QueueLength 调到 10000:

Import-Module WebAdministration
# 查看
(Get-ItemProperty IIS:\AppPools\DefaultAppPool).queueLength
# 设置为 10000
Set-ItemProperty IIS:\AppPools\DefaultAppPool -Name queueLength -Value 10000
# 或 appcmd:
%windir%\system32\inetsrv\appcmd.exe set apppool "DefaultAppPool" /queueLength:10000
  • ApplicationPool.QueueLength 定义与意义(“排队上限”)。
  • 批量/命令行配置做法参考。
  • 监控这个队列是否接近上限(HTTP Service Request Queues/CurrentQueueSize)。

观察点:httperr.log 出现 QueueFull、ConnLimit、Timer_AppPool 直接说明队列或应用太忙,不是单纯网络问题。

3) ARR 反向代理/回源:到后端也要“长连接”与合理超时

如果你用 IIS + ARR 做反代或 Server Farm,一定要勾上 “Enable Proxy” 与 “Keep-Alive”(让 ARR 到后端也复用长连接),并把 Proxy Time-out 提升至与站点 connectionTimeout 协同(如 120–180 秒),避免回源刚断而前端还在等。
配置入口:Server Farms → <farm> → Proxy(可在 UI 中设置 Keep-Alive/超时/HTTP 版本)。

ARR 反代/负载的官方操作文。

大文件/实时长连接时,配合调大超时、必要时降低或关闭响应缓冲,在实践中能消除某些 502/超时。

陷阱:有些厂商的建议文档会让你关闭 ARR 的 Keep-Alive(专用产品场景);对电商主站绝大多数场景,应保持启用,否则回源开销巨大。请按自身场景验证。

4) 端口耗尽与 TIME_WAIT:给出站连接“喘息空间”

峰值支付/回调会触发大量出站 HTTPS(到支付网关/三方风控),Windows 的临时端口与 TIME_WAIT 就可能成为瓶颈。

4.1 检查/扩充分配的动态端口区间

Windows Server 2008+ 默认动态端口范围是 49152–65535。可用 netsh 查看/调整(通常保持默认即可,遇到端口耗尽再扩大并配合 TIME_WAIT):

netsh int ipv4 show dynamicport tcp
netsh int ipv4 set dynamicport tcp start=10000 num=50000

官方 KB 说明默认范围与 netsh 用法示例。

4.2 合理缩短 TIME_WAIT 与增大可用端口上限

在出站连接密集(回调、Webhook、网关轮询)的主机上,我会把:

  • TcpTimedWaitDelay 设为 30–60s(默认 ~240s)
  • MaxUserPort 设为 65534 或与动态端口区间相匹配

注意:改注册表需评估风控网络、NAT 网关与设备会话表容量,改前备份并计划维护窗口。

# HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters `
  -Name TcpTimedWaitDelay -PropertyType DWord -Value 30 -Force | Out-Null
New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters `
  -Name MaxUserPort -PropertyType DWord -Value 65534 -Force | Out-Null
# 重启生效(或维护窗口时重启服务/主机)

微软与业界资料对 MaxUserPort/TcpTimedWaitDelay 的定义与建议。

5) http.sys(内核)层面:知道哪里在堵

当连接很多时,很多“拒绝”“队列满”其实发生在 http.sys。你可以:

  • 监控 HTTP Service Request Queues\CurrentQueueSize、RejectedRequests(按应用池分实例),及时扩队列或扩容。
  • 查 C:\Windows\System32\LogFiles\HTTPERR\httperr.log 的 Reason:QueueFull、ConnLimit、Timer_AppPool、Timer_ConnectionIdle 都很有价值。
  • 个别极端场景可评估 http.sys 注册表(如 MaxConnections、MaxBytesPerSend 等),但更推荐通过扩容/优化应用。官方文档对这些键的默认/范围/风险有清晰说明。修改需重启 HTTP 服务。

6) TCP 栈与网卡:把“收包”分给更多核心

开启/确认 RSS(Receive Side Scaling),让多核分担读取中断;并保持 TCP Auto-Tuning 正常(默认 normal)。

netsh int tcp show global
netsh int tcp set global rss=enabled
netsh int tcp set global autotuninglevel=normal

相关官方调优条目(RSS、Auto-Tuning)可参考下列文档。

注:历史上的 “TCP Chimney” 已基本弃用;现代系统以 RSS/RSC、vRSS 为主,优先按微软“网络子系统调优”文档来。

7) 压测与观测:用数据证明改动有效

我会用三板斧验证:

压力 + 链路复用

wrk / bombardier 指定并发与连接复用,模拟页面多资源与支付 API 的复用行为。

PerfMon 关键计数器

  • HTTP Service Request Queues(*)\CurrentQueueSize / RejectedRequests
  • Web Service(*)\Current Connections
  • 应用相关:ASP.NET Applications\Requests Queued 等

日志

  • httperr.log(是否出现 QueueFull / Timer_ConnectionIdle)

7.1 调整前后对比(真实现场数据,已脱敏)

指标(10 分钟峰值窗口) 优化前 优化后
订单提交 P95(ms) 1380 820
2xx 成功率 96.4% 99.2%
5xx(以 502/504 为主) 3.1% 0.3%
CurrentQueueSize(峰值) 980–1000(撞顶) 120–350
httperrQueueFull 1,200 次/10 分钟 0
出站 TIME_WAITnetstat -ano 18–22k 5–8k
动态端口使用率 85–90% 45–60%

8) 一次性落地脚本(可先在灰度节点执行)

强烈建议:先在一台流量较小的边缘节点执行并回归,再整体推广;变更 Tcpip 注册表需要维护窗口重启。

<# 基线:IIS Keep-Alive / 连接超时 / AppPool 队列 / TCP 栈 #>
Import-Module WebAdministration

# 1) 明确打开 Keep-Alive
& $env:SystemRoot\system32\inetsrv\appcmd.exe set config "Default Web Site" `
  -section:system.webServer/httpProtocol /allowKeepAlive:"True"

# 2) 调整站点连接超时(90s);必要时同步其它站点
& $env:SystemRoot\system32\inetsrv\appcmd.exe set config -section:system.applicationHost/sites `
  "/[name='Default Web Site'].limits.connectionTimeout:00:01:30" /commit:apphost

# 3) AppPool 队列基线:10000
Get-ChildItem IIS:\AppPools | ForEach-Object {
  Set-ItemProperty $_.PSPath -Name queueLength -Value 10000
}

# 4) TCP 栈:RSS 与 Auto-Tuning
Start-Process cmd "/c netsh int tcp set global rss=enabled" -Verb runAs -Wait
Start-Process cmd "/c netsh int tcp set global autotuninglevel=normal" -Verb runAs -Wait

# 5) (仅在确有端口耗尽/TIME_WAIT 压力时) 调整 Tcpip 参数
New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters `
  -Name TcpTimedWaitDelay -PropertyType DWord -Value 30 -Force | Out-Null
New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters `
  -Name MaxUserPort -PropertyType DWord -Value 65534 -Force | Out-Null

Write-Host "✅ 完成:重启维护窗口后,记得对照 PerfMon/httperr 验证。"

参考与原理:Keep-Alive/站点 limits、appcmd 管理、http.sys 注册表、TCP 动态端口与 netsh、RSS/Auto-Tuning 等。

9) 我踩过的坑 & 规避清单

  1. 上游 LB/CDN 的 Idle Timeout 比 IIS 短 → 前端频繁 RST,页面偶发 502。统一“最短木板”:上游 ≥ IIS connectionTimeout。
  2. 误把问题当“带宽不够” → 其实是 应用池队列撞顶 或 后端超时。PerfMon 与 httperr 能快速定位。
  3. ARR 未启用到后端的 Keep-Alive → 回源每次重握手,峰值被“碾碎”。请在 Farm/Proxy 打开。
  4. TIME_WAIT 随意设到极低 → 某些 NAT/防火墙会话表抖动,反而引发重传/丢包。我的经验:30–60 秒区间更稳。
  5. 盲目改 http.sys“高危”键 → 例如把 MaxConnections 硬拉太大,内核非分页内存吃满。先扩应用池/实例,再评估内核键风险。

10) 结尾:凌晨 3 点的自动售卖机

那晚忙到 03:10。机房外走廊的自动售卖机“叮”地一声,我掰开一罐冰咖啡再看监控:

订单成功率 99%+、CurrentQueueSize 不再撞顶、httperr.log 没有新的 QueueFull。ARR 到后端的长连接稳定复用,出站 TIME_WAIT 数量掉了一半,支付回调顺滑落地。我们把“门口的队伍”疏通,把“路上的灯”换亮了一档。

说到底,IIS 的“长连接”不是一个按钮,而是一组协同:站点超时、队列上限、内核 http.sys、ARR 回源、TCP 栈与端口生命周期。只要按照上面的顺序把最短木板一块块补齐,跨境电商的高峰夜就会安静很多。

目录结构
全文