.NET Framework 是否具有独立于操作系统的全局 DNS 缓存?

Sta*_*rev 8 dns high-availability

介绍

首先,我尝试了 C# DNS 相关 SO 线程和其他互联网文章中的所有建议 - 弄乱ServicePointManager /ServicePoint 设置、通过 HTTP 标头设置自动请求连接关闭、更改连接租用时间 - 没有任何帮助。似乎所有这些设置都是为了修复长时间运行的进程(如 Web 服务)中的 DNS 问题。如果进程拥有自己的 DNS 缓存以最大程度地减少 DNS 查询或操作系统 DNS 缓存读取,这甚至是有意义的。但这不是我的情况。

问题

我们的生产基础设施使用 HA(高可用性)DNS 在维护或功能问题期间交换服务器节点。它的构建方式是,在某些地方我们有多个 CNAME 记录,它们实际上指向同一个 HA A 记录,如下所示:

  • eu.site1.myprodserver.com (CNAME) > eu.ha.myprodserver.com (A)
  • eu.site2.myprodserver.com (CNAME) > eu.ha.myprodserver.com (A)

所有这些记录的 TTL 均为 60 秒。因此,当欧洲节点出现故障或维护时,A记录会切换到其他节点的IP地址。

然后我们有一个监控实用程序,每 5 分钟执行一次并使用 site1 和 site2。为了使其正常工作,两个名称必须指向同一个 DC,因为 DC 之间的数据同步不会那么快。由于两个 CNAME 实际上都链接到同一个具有短 TTL 的 A 记录,乍一看似乎不会出错。但事实证明它可以。

该实用程序是用适用于 .NET Framework 4.7.2 的 C# 编写的,并使用HttpClient 类向两个站点执行请求。是的

我们注意到,当发生服务器节点切换时,该实用程序通常开始表现得好像站点 1 和站点 2 位于不同的 DC 中一样。它在这种时刻的行为模式是严格确定的,因此它不会在过程中间的某个地方感到困惑 - 它从一开始就错误地解析了这些地址中的一个或两个。

我制作了另一个更简单的实用程序,它只向 site1 发送一个 GET 请求,然后开始有意地打开和关闭节点并运行该实用程序以查看哪个 DC 将满足其请求。结果非常令人沮丧。

尽管 Windows DNS 缓存已更新(通过 cmdlet 检查ipconfigGet-DnsClientCache,并且尽管总体记录的 TTL 为 60 秒,但 HttpClient 有时仍会持续向旧 IP 地址发送请求 15-20 分钟。即使我完全关闭了“过时的”应用程序服务器,该实用程序仍会继续尝试连接到它,因此即使连接失败也不会唤醒它。

ipconfig /flushdns如果您在实用程序运行之间开始运行,情况会变得更加令人沮丧。有时在flushdns之后,实用程序意识到IP已经改变。但是一旦你创建另一个flushdns(或者甚至不需要 - 我还没有100%清楚地弄清楚这一点)并再次运行该实用程序 - 它就会回到旧地址!难以置信!

并增加更多的挫败感。如果您在调用 HttpClient 之前使用Dns.GetHostEntry方法(根据此注释使用缓存)从同一实用程序中解析 IP 地址,则解析结果将是正确的...但是 HttpClient 无论如何都会建立到 IP 的连接似乎是他自己独立选择的地址。因此 HttpClient 在某种程度上似乎并不依赖于内置的 .NET Framework DNS 解析。

所以问题是:

  1. 新创建的 .NET Framework 进程从哪里获取这些缓存的 DNS 结果?
  2. 即使存在某种神秘的全局 .NET 特定 DNS 缓存,那么为什么它绝对忽略 TTL?
  3. 在它已经“理解”地址已更改之后,它怎么可能恢复到过时的旧 IP 地址呢?

PS 我已经通过实现一个自定义的HttpClientHandler来解决这一切,该自定义 HttpClientHandler 对每个主机名的第一次使用执行 DNS 查询,因此它独立于外部 DNS 缓存(中间 DNS 服务器的缓存除外,这也会在一定程度上影响事物)。但这在 TLS 证书验证方面有点棘手,而且最终的解决方案似乎还没有准备好投入生产 - 但我们仅将其用于监控,所以对我们来说没关系。如果有人对此感兴趣,我将展示与此答案的示例有些相似的类代码。

更新2021-10-08

该实用程序在公司代理后面运行。事实上,有多个代理用于负载平衡。所以我现在也在验证这一点:

  • 如果 DNS 解析是由代理执行的,并且它们不遵守 TTL,或者它们通过主机名缓存(保持活动状态)TCP 连接 - 这将解释整个问题
  • 如果不同的代理可以在该实用程序的不同运行中处理 HTTP 请求 - 这将回答最令人沮丧的问题 #3

更新2021-10-15

“.NET Framework 是否具有独立于操作系统的全局 DNS 缓存?”的答案 没有。一般来说,HttpClient 类或 .NET Framework 与这一切无关。将我的调查结果发布为已接受的答案。

Sta*_*rev 5

HttpClient,请见谅!这不是你的错!

嗯,这次调查的规模很大。我必须将答案分成两部分,因为事实证明存在两个不相关的问题。

1.代理服务器问题

正如我所说,该实用程序是在公司代理后面进行测试的。如果您还不知道(就像我最近才知道)使用代理服务器时,这不是您的计算机在执行 DNS 查询,而是代理服务器为您执行此操作。

我进行了一些测量,以了解在 DNS 记录切换后实用程序保持连接到错误 DC 的时间有多长。答案是奇妙的 30 分钟。这个实验还清楚地表明,本地 Windows DNS 缓存与此无关:这 30 分钟正好从代理服务器唤醒时开始最终开始向正确的 DC 发送 HTTP 请求)。

30 分钟的确切数字帮助我们的一位管理员最终发现代理服务器具有最小 DNS TTL 的配置参数,默认情况下设置为 1800 秒。所以代理有自己的 DNS 缓存。这些是硬件思科代理,管理员还注意到该参数“隐藏得相当深”,甚至在用户手册中都没有提及。

一旦最小代理的 DNS TTL 从 1800 秒更改为 1 秒(是的,管理员毫不留情),问题就停止在我的计算机上重现。

但是,如果“忘记”刚刚理解的正确 IP 地址并退回到旧的 IP 地址呢?

出色地。正如我也说过的,有几个代理。有一个公司代理 DNS 名称,但如果您运行nslookup它 - 它会显示其后面的多个 IP。每次解析代理服务器的 IP 地址时(例如本地缓存过期时),您很有可能会跳转到另一台代理服务器。

这正是ipconfig /flushdns我一直在做的事情。当我开始使用代理服务器使用其直接 IP 地址而不是通用 DNS 名称时,我发现不同的代理可以轻松地将相同的请求路由到不同的 DC。这是因为其中一些拥有 30 分钟缓存的 DNS 记录,而另一些则必须执行解析。

不幸的是,在代理理论被证明之后,又传来了另一个消息:生产监控服务器放置在公司网络之外,并且不使用任何代理服务器。那么我们开始吧...

2. 短TTL和公共DNS服务器问题

监控服务器配置为使用 8.8.8.8 和 8.8.4.4 Google 的 DNS 服务器。来自这些服务器的短期 DNS 记录的解析响应有些奇怪:

  • 返回的 CNAME 记录的 TTL 在 1 小时左右波动。它会在几分钟内逐渐减少,然后跳回 3600 秒 - 依此类推。
  • 根 A 记录的返回 TTL 几乎总是正好 60 秒。我偶尔会收到各种小于 60 的数字,但没有任何明显的人类可感知的逻辑。因此,看起来这些 IP 地址实际上指向平衡器,该平衡器在多个相似的 DNS 服务器之间分配请求,这些服务器彼此不同步(并且每个服务器都有自己的缓存)。

Windows 并不愚蠢,根据我的实验,它不关心 CNAME 的 TTL,只关心根 A 记录 TTL,因此即使是 CNAME 记录,其客户端缓存也不会分配高于 60 秒的 TTL。

但由于 Google 服务器返回的 A 记录 TTL 不一致(或者在某种意义上过度一致性?)(不可预测的 0-60 秒),Windows 本地缓存会变得混乱。有两个事实可以证明这一点:

  • 在几分钟内多次调用Resolve-DnsNamesite1 和 site2,并在调用之间随机暂停,最终导致Get-ClientDnsCache显示两个站点名称的本地缓存 TTL 相差最多 15 秒。这个差异足够大,有时会把事情搞砸。这只是我的简短实验,所以我很确定它实际上可能会变得更大。
  • 在切换 DNS 记录的同时,每隔 3-5 秒对每个站点执行Invoke-WebRequest一次,让我两次面临请求发送到不同 DC 的情况。

后一个实验有一个我无法解释的奇怪细节。Get-DnsClientCache之后调用Invoke-WebRequest显示本地缓存中没有出现刚刚请求的站点名称的记录。但无论如何,问题显然已经重现。

结论?

需要时间来看看我的实时 DNS 解析解决方法是否会带来任何改进。不幸的是,我不相信它会 - 生产中使用的 DNS 服务器(最终将由监控实用程序用于实时 IP 解析)是公共 Google DNS,在我的情况下并不可靠。

比间歇性失败的监控实用程序更糟糕的一件事是,现实世界的用户也依赖公共 DNS 服务器,他们在我们的维护工作或重大故障期间肯定会遇到问题。

那么我们从这一切中学到了什么吗?

  • 也许较短的 DNS TTL 通常是一种不好的做法?
  • 也许我们应该安装额外的路由器,为它们分配静态 IP,将 DNS 名称附加到它们,然后在 DC 之间内部路由流量,以最终停止依赖 DNS 记录更改?
  • 或者也许公共 DNS 服务器做得不好?
  • 或者也许技术奇点比我们想象的更接近?

我不知道。但很可能“是”是所有这些问题的正确答案。

然而,我们确实学到了一件事:网络硬件制造商应该更好地编写他们的文档。