当前位置: 首页 > news >正文

【Azure App Service】应用服务中的源网络地址转化(SNAT: Source Network Address Transfer)

App Service 应用经常需要访问外部服务,比如 Azure SQL、Redis、Storage 或第三方 API。很多人会以为应用是直接从 worker 实例访问公网,但实际上并不是这样。

App Service 的 worker 实例运行在 scale unit / stamp 内部,通常没有直接分配公网 IP。它访问外部公网 endpoint 时,需要经过 stamp 的出站负载均衡器。这个负载均衡器会把 worker 的私网源地址和端口,转换成公网源地址和端口,这个过程就是 SNAT(Source Network Address Translation)

这篇文章主要整理 App Service 中排查 SNAT 问题时需要知道的几件事:SNAT 如何工作、SNAT 端口为什么会耗尽、端口如何分配、耗尽时有哪些症状,以及应用应该如何优化连接使用。

 

SNAT 是怎么工作的

以 TCP 连接为例,一次出站访问大致是这样发生的:

image

负载均衡器会维护一条映射记录,例如:

字段示例
协议 TCP
Worker 实例地址 10.0.5.60:51014
负载均衡器公网地址 13.76.245.72:12481
外部服务地址 52.189.232.180:80
  • 应用看到的是自己连到了外部服务
  • 外部服务看到的是负载均衡器的公网地址
  • 负载均衡器负责在两边之间做地址转换
  • 这个过程对应用和外部服务都是透明的

 

SNAT 端口耗尽

SNAT 端口的消耗和 TCP 五元组有关:

字段含义
Protocol 协议,例如 TCP
Source IP 源 IP,SNAT 后是负载均衡器公网 IP
Source Port 源端口,也就是 SNAT 端口
Destination IP 外部目标 IP
Destination Port 外部目标端口
  • 如果多个 TCP 流访问的是同一个目标 IP、同一个目标端口、同一个协议,那么它们需要不同的源端口来区分。也就是说,高并发访问同一个外部服务时,SNAT 端口会很快被消耗
  • 反过来,如果多个流访问的是不同目标 IP 或不同端口,那么五元组本身已经不同,SNAT 端口就有机会被复用。

每个 IP 地址最多只能打开有限数量的端口。

典型的 App Service stamp 会有多个出站 IP,但这些端口需要被同一个 stamp 内的所有实例共享。Web App、WebJob、Function、Application Insights 遥测流量等,都可能使用同一组 SNAT 资源。

 

如果应用频繁打开和关闭连接,情况会更严重。因为 SNAT 端口关闭后不会马上释放:

关闭方式SNAT 端口释放时间
正常 FIN/ACK 关闭 约 240 秒后释放
RST 重置 约 15 秒后释放
达到 idle timeout 按 idle timeout 释放

这意味着,如果一个 Web 应用每秒打开 1 条 HTTP 连接,调用后端服务后正常关闭,那么在 240 秒内可能累计占用约 240 个 SNAT 端口。

另一个例子是数据库连接池。如果一个繁忙站点的 SQL 连接池大小是 300,并且数据库查询执行较慢,那么这些连接可能持续占用约 300 个 SNAT 端口。

还有一种常见情况是队列触发的 Function App。如果压测一开始就把大量消息一次性灌入队列,Function 可能瞬间启动大量到 Storage 或其他外部服务的连接,很快耗尽 SNAT 端口。

 

SNAT 端口分配算法

为了避免某个站点耗尽整个 stamp 的 SNAT 端口并影响其他站点,Azure Load Balancer 需要对 SNAT 端口做分配控制。

 

常见算法包括:

分配方式端口数量
On-demand 算法 每实例基础 160 个,可按需尽力分配更多
新算法 每实例固定预分配 128 个

这里的 160 个 SNAT 端口 可以用一个粗略的容量分摊思路来理解。一个典型的 App Service stamp 可能有 5 个出站 IP,每个 IP 理论上有大约 65536 个端口。如果这些端口要被 stamp 内大约 2000 个实例共享,那么每个实例能稳定分到的端口数量大约是:

5 × 65536 ÷ 2000 ≈ 163.84   取整后就接近 160 个端口 / 实例。

这不是说每个实例永远只能使用 160 个端口,而是 On-demand 算法下的一个基础保障值,如果 stamp 里还有空闲端口,负载均衡器可能会尽力分配更多。

On-demand 算法的复杂之处在于“按需”。如果负载均衡器还有足够端口,它可能会给某个实例分配超过基础值的端口,但当 stamp 变得繁忙时,同样的应用下次可能无法再拿到那么多端口。

因此,从“可保证”的角度看,一个 worker 实例应按 128 个 SNAT 端口来设计

只要应用每实例使用不超过这个范围的外部连接,负载均衡器通常就不会因为 SNAT 端口耗尽而阻止它访问外部 endpoint。

 

SNAT 端口耗尽时的症状

当 SNAT 端口耗尽时,应用常见表现包括:

  • 连接外部 endpoint 变慢;
  • 请求长时间 pending;
  • 最终出现 socket timeout;
  • Application Insights 里能看到依赖调用失败。

如果启用了 Application Insights dependency tracking,也可能看到外部依赖调用失败。

 

如何解决 App Service 的 SNAT 端口耗尽

总体方向是:先减少不必要的连接占用,再考虑扩展。

image

具体建议:

  1. 复用连接:不要每次请求都 new 一个 HttpClient
  2. 使用连接池:数据库、HTTP 客户端都应合理复用连接;
  3. 控制连接池大小:连接池不是越大越好,过大的池会持续占用端口;
  4. 降低重试强度:失败时疯狂重试会进一步放大端口占用;
  5. 让后端尽快响应:后端越慢,连接存活越久,SNAT 端口占用越久;
  6. 横向扩容 App Service Plan:SNAT 端口按实例分配,实例变多,总可用端口也会增加;
  7. 使用 App Service Environment:ASE 的实例池更小,worker 实例通常可以获得更多 SNAT 端口;
  8. 压测要贴近真实流量:负载测试应以稳定速度投喂数据,而不是一开始就把所有消息一次性灌入队列。

 

示例代码及优化

下面的代码可以复现 SNAT 端口耗尽问题:

public string Index(string url)
{var request = HttpWebRequest.Create(url);request.GetResponse();return "OK";
}

为了复用连接,可以改成关闭响应对象:

public string Fin(string url)
{var request = HttpWebRequest.Create(url);var response = request.GetResponse();response.Close();return "OK";
}

 

下面这种写法也会造成 SNAT 端口泄漏,因为每次调用都会创建新的 HttpClient

public async Task<string> Client(string url)
{using (var client = new HttpClient()){await client.GetAsync(url);}return "OK";
}

 

可以改成复用同一个 HttpClient

private static Lazy<HttpClient> _client = new Lazy<HttpClient>();public async Task<string> ReuseClient(string url)
{var client = _client.Value;await client.GetAsync(url);return "OK";
}

 

常见问题(FAQ):

Q:我能自己看到 App Service 的 SNAT 端口分配指标吗?
A:一般情况下,这个指标不直接公开。日常设计时不要依赖“实际能拿到多少端口”,而应按每实例 128 个的保守值来控制。

Q:为什么不能直接根据 SNAT 端口指标做自动扩缩容?
A:因为很多 SNAT 问题本质是连接没有复用。如果代码持续浪费连接,单纯扩容只是把问题摊开,并不一定治本。应该先优化连接复用和后端响应,再考虑扩容。

Q:SNAT 耗尽和 TCP Connections 耗尽有什么区别?
A:TCP Connections 是 worker 实例层面的连接计数,SNAT 是出站负载均衡器上的公网源端口资源。前者统计所有 TCP 连接,后者只和外部网络流量有关。二者有关联,但不能互相替代。

Q:多个 WebJob 共用同一个 App Service Plan,怎么判断谁占用了最多连接?
A:如果没有按进程维度的连接指标,可以把部分 WebJob 移到另一个 App Service Plan,通过隔离法观察问题是否缓解,逐步定位高连接消耗的任务。

 

参考资料

SNAT with App Service :https://4lowtherabbit.github.io/blogs/2019/10/SNAT/

http://www.jsqmd.com/news/944373/

相关文章:

  • G-Helper终极指南:华硕笔记本轻量级控制工具完全使用教程
  • 实战教程:用vidore/colpali-v1.3-hf构建企业级文档检索系统,附相似度计算代码
  • OpenCore Legacy Patcher终极指南:三步让老Mac焕发新生的免费方案
  • 别只顾着写代码:AI 时代 Builder 的「词元经济学」与成本自查清单
  • 手把手教你用Python的classification_report:从混淆矩阵到业务报告,避坑指南全在这
  • NSC_BUILDER:Nintendo Switch游戏文件管理的终极解决方案
  • 百考通:AI智能化一键生成期刊论文写作,让学术创作更高效
  • 2026浙江GEO服务商实力十强榜单出炉,头部企业引领全省AI营销新发展 - 浙江稻盛和夫
  • 基于Arduino的恒流负载电池容量测试仪设计与制作
  • 终极浏览器音乐解锁指南:10分钟让加密音乐重获自由 [特殊字符]
  • 2026上海全屋漏水维修避坑!厨卫阳台楼顶外墙修缮测评 - 苏易修缮
  • 【智能足迹治理黄金标准】:全球TOP7科技公司正在封测的AI工具整合模型(附内部评估矩阵)
  • 如何高效使用RcloneBrowser:开源跨平台rclone图形界面完全指南
  • 沈阳!家里瓷砖空鼓,翘边怎么办?别着急!2026瓷砖空鼓专业维修公司TOP5口碑与专业度调研,卫生间空鼓翘边,厨房空鼓翘边,客厅空鼓翘边,最新深度调研解析 - 防水资讯
  • 别再傻傻用put了!Java Map的compute三兄弟(compute/computeIfAbsent/computeIfPresent)保姆级使用指南
  • 解放小爱音箱:用XiaoMusic打造你的专属智能音乐管家
  • 5分钟掌握AI金融分析:TradingAgents-CN多智能体股票分析平台完全指南
  • 刘诗诗代言赋能品牌销量,实打实商业带货力落地
  • 2026上海卫生间漏水怎么办?微创补漏维修哪家公司靠谱 - 苏易修缮
  • 2026上海楼顶屋面雨天漏水!反复渗水返修怎么解决?优选榜单 - 苏易修缮
  • 革命性NLP预训练模型electra-small-discriminator:用判别器革新文本编码的终极指南
  • OpenCore Legacy Patcher终极方案:让老旧Mac焕发新生的完整教程
  • 字节火山引擎上调MaaS营收目标至150亿,视频模型Seedance 2.0成增长关键
  • CLIP-ReID实战:基于视觉语言模型的高效图像重识别技术深度解析
  • YOLOv3实战避坑指南:用PyTorch复现时,Binary Cross-Entropy Loss和Anchor聚类到底该怎么配置?
  • OpenCore Legacy Patcher:老旧Mac硬件兼容性修复与macOS现代化升级的技术方案
  • 2026苏州成人在职学历提升靠谱机构盘点|本土成考优选深度测评指南 - 学历提升信息早知道
  • 【2026 年 06 月】PP管配件优质生产厂家推荐指南|PP管件 / PPH配件 / FRPP管件优选 - 多才菠萝
  • 大连!家里瓷砖空鼓,翘边怎么办?别着急!2026瓷砖空鼓专业维修公司TOP5口碑与专业度调研,卫生间空鼓翘边,厨房空鼓翘边,客厅空鼓翘边,最新深度调研解析 - 防水资讯
  • 保姆级教程:从零在Windows上用PyCharm复现TransUNet(含数据集处理完整代码)