【问题标题】:Does .NET Framework have an OS-independent global DNS cache?.NET Framework 是否具有独立于操作系统的全局 DNS 缓存?
【发布时间】:2021-11-26 13:58:53
【问题描述】:

简介

首先,我已经尝试了来自 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-record 会切换到其他节点的 IP 地址。

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

该实用程序是用 C# 为 .NET Framework 4.7.2 编写的,并使用 HttpClient class 执行对两个站点的请求。 Yeah, it's him again.

我们注意到,当发生服务器节点切换时,该实用程序通常会开始像 site1 和 site2 在不同的 DC 中一样运行。它在这些时刻的行为模式是严格确定的,因此它不会在过程中间的某个地方混淆 - 它从一开始就错误地解析了这些地址中的一个或两个。

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

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

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

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

所以问题是:

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

附:我通过实现一个自定义的HttpClientHandler 来解决这个问题,它对每个主机名的第一次使用执行 DNS 查询,因此它独立于外部 DNS 缓存(除了在中间 DNS 服务器上缓存,这也会在一定程度上影响事物)。但这在 TLS 证书验证方面有点棘手,最终解决方案似乎还没有准备好生产 - 但我们只将它用于监控,所以对我们来说没关系。如果有人对此感兴趣,我将展示类似于this answer's example 的类代码。

2021 年 10 月 8 日更新

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

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

2021 年 10 月 15 日更新

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

【问题讨论】:

  • DNS 通常由操作系统处理,而不是由 .NET 处理。
  • @Neil 哦,我希望这一直是真的。正如我在帖子中所说,我确实验证了 Windows 的 DNS 客户端缓存并且它按预期工作。至少这是 ipconfig 和 Get-DnsClientCache 向我展示的方式:Windows 缓存中记录的 TTL 始终
  • 如果你按照代码往下走,你会到达Dns.GetHostAddresses,它会调用操作系统。它可能是一个中间解析器,它正在缓存而不是遵守 TTL
  • @Charlieface 正如我在帖子中所说,我还测试了从实用程序中调用 Dns.GetHostEntry 方法,它返回了正确的(实际)结果 - 就像 Windows 实用程序一样。我会看看Dns.GetHostAddresses 是否返回不正确的内容。
  • @Am_I_Helpful 是的,我确实在没有代理的情况下尝试过,但问题没有重现。我什至发现了一些代理服务器搞砸的间接证据。我现在正积极与我们的代理服务器管理员合作,以弄清楚那里到底发生了什么。一旦出现问题,我将发布更新(希望对我自己的帖子有详细的已接受答案)。

标签: dns high-availability


【解决方案1】:

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

嗯,这次调查是巨大的。而且我必须将答案分成两部分,因为结果是两个不相关的问题。

1。代理服务器问题

正如我所说,该实用程序正在公司代理后面进行测试。如果您不知道(比如我直到最近几天才知道)何时使用代理服务器,则不是您的机器执行 DNS 查询 - 是代理服务器为您执行此操作。

我进行了一些测量,以了解该实用程序在 DNS 记录切换后持续连接到错误 DC 的时间。答案是完美的 30 分钟。这个实验还清楚地表明,本地 Windows DNS 缓存与它无关:这 30 分钟正是在代理服务器唤醒(终于开始向正确的 DC)。

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

只要最小代理的 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 本地缓存会变得混乱。有两个事实证明了这一点:

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

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

结论?

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

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

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

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

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

但是我们肯定学到了一件事:网络硬件制造商应该更好地编写他们的文档。

【讨论】:

    猜你喜欢
    • 2013-06-26
    • 1970-01-01
    • 1970-01-01
    • 2011-06-09
    • 1970-01-01
    • 2013-06-14
    • 1970-01-01
    • 2010-12-22
    • 1970-01-01
    相关资源
    最近更新 更多