Envoy 上游 DNS 解析架构详解:四种可插拔 DNS 解析器、配置参数与统计指标
2026/9/14 9:59:38 网站建设 项目流程

Envoy 上游 DNS 解析架构详解:四种可插拔 DNS 解析器、配置参数与统计指标

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

Envoy 中大量组件都依赖 DNS 解析:STRICT_DNS / LOGICAL_DNS 集群、动态正向代理(dynamic forward proxy)系统、UDP DNS 过滤器等。本篇基于仓库架构文档 DNS Resolution 展开,结合 DNS 解析器接口、工厂选择逻辑 和 c-ares 解析器配置 proto,完整讲清 Envoy 的 DNS 解析是如何被抽象为可插拔扩展、四种内置解析器各自的行为差异、c-ares 解析器的全部配置参数及默认值,以及dns.cares/dns.apple/dns.hickory统计树下的可观测指标。读完本文,你可以准确选择解析器扩展、写出可复制的typed_dns_resolver_config,并依据统计数据诊断解析失败、超时与通道重初始化等问题。

一、谁在用 DNS 解析:Envoy 中的 DNS 消费方

根据架构文档,Envoy 中有多类组件会发起 DNS 解析:

  • 集群类型STRICT_DNS(严格 DNS,必须解析成功才加入 host)与LOGICAL_DNS(逻辑 DNS,域名本身作为一个逻辑主机)两种上游集群类型;
  • 动态正向代理系统:由集群与 HTTP 过滤器组合而成,按请求目标域名动态建立上游连接,其 DNS 缓存由typed_dns_resolver_config字段配置解析器,可参考 HTTP 过滤器文档;
  • UDP DNS 过滤器:作为 DNS 代理转发查询时也需要底层解析能力。

这些消费方在源码中的落点可以相互印证:集群侧在 cluster_factory_impl.cc 中根据集群类型构建解析器;动态正向代理的 DNS 缓存在 dns_cache_impl.cc 中创建;UDP 过滤器在 dns_filter.cc 中创建。所有消费方并不直接依赖某个具体解析库,而是依赖统一的Network::DnsResolverFactory接口。

二、可插拔抽象:DnsResolver 接口与 DnsResolverFactory

从源码结构看,整个 DNS 子系统的抽象核心是两个接口文件:

  1. DnsResolverFactory:继承自Config::TypedFactory,每个解析器扩展注册一个工厂,其createDnsResolver(dispatcher, api, typed_dns_resolver_config)方法基于 typed 配置实例化具体解析器。dispatcher是本地工作线程的事件分发器,api用于访问系统资源。
  2. DnsResolver:异步解析器抽象,核心方法有:
    • resolve(dns_name, dns_lookup_family, callback):发起异步解析,返回ActiveDnsQuery*句柄,调用方可用于取消查询;
    • resetNetworking():提示解析器重置网络状态,典型场景是网络切换(如 WiFi 切到蜂窝网络),具体行为由各解析器自行决定,可能涉及重建解析连接、重读解析目标等;
    • 回调类型ResolveCb接收ResolutionStatusCompleted/Failure)、details字符串以及解析出的地址/TTL 列表。值得注意的实现细节:Completed并不代表“解析到地址”,文档注释明确说明 NODATA、SERVFAIL、NONAME 等都属于“查询完成但无结果”的语义,因此Completed才是更准确的表达。

与解析结果相关的几个数据结构同样定义在 dns.h:

  • AddrInfoResponse:A/AAAA 记录,包含地址与 TTL;
  • SrvResponse:SRV 记录,包含 host、port、priority、weight;
  • DnsLookupFamily枚举:V4OnlyV6OnlyAutoV4PreferredAll,控制 IP 版本查询策略;
  • ActiveDnsQuery::CancelReason:区分QueryAbandoned(调用方不再需要结果)与Timeout(调用方视角超时,解析器可能借此销毁现有连接以便后续查询更快得到答案)。

三、四种内置 DNS 解析器扩展

Envoy 通过可插拔扩展提供 DNS 解析,默认使用 c-ares 作为解析库。仓库内置 4 个解析器扩展,源码分别位于 source/extensions/network/dns_resolver/ 下的cares/apple/getaddrinfo/hickory/子目录,配置 proto 位于 api/envoy/extensions/network/dns_resolver/:

解析器扩展名类型配置消息适用平台/特点
c-aresenvoy.network.dns_resolver.caresCaresDnsResolverConfig默认实现,纯 C 异步 DNS 库
Appleenvoy.network.dns_resolver.appleAppleDnsResolverConfig仅 iOS/macOS,通过 Apple 专用 API 解析
getaddrinfoenvoy.network.dns_resolver.getaddrinfoGetAddrInfoDnsResolverConfig基于系统getaddrinfo,无解析器专属统计
Hickory DNSenvoy.network.dns_resolver.hickoryHickoryDnsResolverConfig纯 Rust 解析器,支持 DoT/DoH/DNSSEC

3.1 c-ares(默认)

c-ares 解析器实现位于 cares/dns_impl.h。从源码结构看,它维护一个ares_channel,所有调用与回调都假定发生在拥有创建用 dispatcher 的线程上;resetNetworking()的实现就是reinitializeChannel(),即重建 c-ares 通道。此外还包含以下机制,均可在头文件成员变量中找到对应物:

  • UDP 通道周期刷新udp_channel_refresh_timer_配合max_udp_channel_duration定期重建通道,避免陈旧 socket 状态并改善 UDP 端口负载分布;
  • 双栈解析AddrInfoPendingResolutiondual_resolution_逻辑——当dns_lookup_familyV4PreferredAuto时,首次解析失败后发起第二次解析;为All时则对两个 IP 族并发查询;
  • 不可路由地址族过滤filter_unroutable_families开启时,解析器会查询本机网络接口可用性,若没有 IPv4 接口就不再返回 IPv4 地址。

3.2 Apple(iOS/macOS)

Apple 解析器实现在 apple/apple_dns_impl.h,仅当在 Apple 系统上构建时才编译。它可通过 runtime 特性envoy.restart_features.use_apple_api_for_dns_lookups在 Apple 系统上替代默认解析路径(详见下文的工厂选择逻辑)。

3.3 getaddrinfo

实现位于 getaddrinfo/getaddrinfo.cc,直接委托系统的getaddrinfoAPI。架构文档特别指出:getaddrinfo 解析器目前不产生解析器专属统计,这是它与另外三种实现的可观测性差异,监控时需注意。

3.4 Hickory DNS(纯 Rust)

Hickory 是一个基于 Hickory DNS 库的纯 Rust 解析器,支持标准 DNS(UDP/TCP)、DNS-over-TLS(DoT)、DNS-over-HTTPS(DoH)与 DNSSEC 校验。从源码结构看(hickory/hickory_dns_impl.h),它通过 Envoy 的动态模块(dynamic modules)框架接入:HickoryDnsResolverConfig保存的是一组从动态模块解析出来的函数指针(on_dns_resolver_config_new_on_dns_resolver_new_等),而不是直接调用 Rust 代码。解析器在其自己的 Tokio runtime 线程上异步运行,独立于 Envoy 的事件循环,因此 DNS 解析不会阻塞 dispatcher 线程。

四、解析器如何选择:工厂选择优先级

不同消费方(集群、DNS 缓存、UDP 过滤器)最终都通过 dns_factory_util.h 中的makeDnsResolverConfig()把各自异构的配置归一化为一个TypedExtensionConfig。从源码的分支顺序看,选择优先级如下:

  1. typed_dns_resolver_config显式指定:配置中已存在 typed 解析器配置时,原样采用,优先级最高;
  2. Apple 系统专用 APItryUseAppleApiForDnsLookups()检测平台与 runtime 特性envoy.restart_features.use_apple_api_for_dns_lookups,满足则生成AppleDnsResolverConfig
  3. dns_resolution_config:配置中存在该字段时,将其resolversdns_resolver_options映射进CaresDnsResolverConfig
  4. 遗留字段兜底handleLegacyDnsResolverData()为向后兼容复制旧字段(如 bootstrap 与 DNS 缓存配置上的use_tcp_for_dns_lookups),并始终生成 c-ares typed 配置。

若完全未指定解析器配置,则调用createDefaultDnsResolverFactory():从源码注释看,默认行为是macOS 上用 Apple 解析器,其他平台一律 c-ares。这一机制解释了架构文档中“默认使用 c-ares,Apple 系统额外提供 Apple API 路径”的描述。

五、c-ares 解析器完整配置参数

CaresDnsResolverConfig定义在 cares_dns_resolver.proto,是内置 DNS typed 配置的典型示例。各字段说明如下:

字段类型说明
resolversrepeated Address自定义 DNS 服务器地址列表;是否覆盖系统默认由use_resolvers_as_fallback决定
use_resolvers_as_fallbackbooltrue时仅当 c-ares 无法从系统(如/etc/resolv.conf)获得 nameserver 才使用resolvers;否则resolvers覆盖系统默认。默认false
filter_unroutable_familiesbool查询本机接口可用性,过滤掉无对应网络接口地址族的解析结果(如无 IPv4 接口则不返回 IPv4 地址)
dns_resolver_optionsDnsResolverOptions见下节
udp_max_queriesUInt32Value限制基于 UDP 的 DNS 查询数量上限(当前仅 c-ares 解析器适用)
query_timeout_secondsUInt64Value每个 name server 首次响应一个查询的超时秒数,最小 1。注意:c-ares 库默认 2 秒,而Envoy 未设置时的默认值是 5 秒,这是为维持既有行为而做的调整
query_triesUInt32Value放弃前的最大查询尝试次数,每次尝试可能使用不同 name server,最小 1。c-ares 库默认 3 次,Envoy 默认 4 次
rotate_nameserversbool启用后对 name server 做轮转选择以均衡查询负载;禁用(默认)时按配置顺序尝试。该设置覆盖系统的 name server 轮转配置
edns0_max_payload_sizeUInt32Value最大 EDNS0 UDP 载荷字节数,取值 512–4096。推荐值 1232(安全默认,避免分片)或 4096(最大允许);不设置时由 c-ares 内部默认(通常 1232)
max_udp_channel_durationDuration设置后 DNS 解析器会在该时长周期性地重新初始化 c-ares 通道,帮助摆脱陈旧 socket 状态、改善 UDP 端口负载分布;不设置则不做周期刷新
reinit_channel_on_timeoutbooltrue时当查询以ARES_ETIMEOUT失败就重新初始化通道,可快速恢复偶发的 UDP socket 不可用;在网络抖动导致超时的环境中会增加通道重建频率,此时更建议改用max_udp_channel_duration做周期刷新。默认false
qcache_max_ttlUInt32Valuec-ares 内部 DNS 响应缓存的最大 TTL(秒),取值 ≥ 0;设为非零值时启用查询缓存并尊重响应 TTL(不超过该上限)。注意 c-ares 库默认缓存 1 小时,而Envoy 该字段默认 0,即完全禁用查询缓存

5.1 DnsResolverOptions

dns_resolver_options指向 resolver.proto 中的DnsResolverOptions,包含两个开关:

  • use_tcp_for_dns_lookups:所有 DNS 查询使用 TCP 而非默认 UDP;
  • no_default_search_domain:不使用默认搜索域,仅按原样查询主机名或其别名。

同一 proto 中的DnsResolutionConfigresolvers+dns_resolver_options)则对应上文选择优先级中的第 3 级,用于非 typed 的集群/DNS 缓存配置场景。

六、统计指标:dns.cares / dns.apple / dns.hickory

三种解析器各自输出独立的统计树,与源码中的统计宏定义一一对应(如 cares/dns_impl.h 的ALL_CARES_DNS_RESOLVER_STATS)。

6.1 c-ares(dns.cares统计树)

名称类型说明
resolve_totalCounterDNS 查询总数
pending_resolutionsGauge当前未完成的 DNS 查询数
not_foundCounter返回NXDOMAINNODATA的查询数
get_addr_failureCounterDNS 查询过程中的一般性失败数
timeoutsCounter超时的查询数
reinitsCounterc-ares 通道重新初始化次数

其中reinitstimeouts组合特别有用:若reinit_channel_on_timeout开启,超时会触发通道重建,reinits的持续增长可以帮助判断 UDP socket 健康问题。

6.2 Apple(dns.apple统计树)

名称类型说明
connection_failureCounter连接 DNS 服务器的失败尝试数
get_addr_failureCounter调用 GetAddrInfo API 时的一般性失败数
network_failureCounter因网络连通性导致的失败数
processing_failureCounter处理来自 DNS 服务器数据时的失败数
socket_failureCounter获取到 DNS 服务器的 socket 文件描述符的失败尝试数
timeoutCounter超时的查询数

6.3 Hickory(dns.hickory统计树)

名称类型说明
resolve_totalCounter已完成的 DNS 查询数
pending_resolutionsGauge当前在途(in-flight)的 DNS 查询数
not_foundCounter返回NXDOMAINNODATA响应的查询数
get_addr_failureCounterDNS 查询过程中的一般性失败数
timeoutsCounter超时的查询数

6.4 getaddrinfo

getaddrinfo 解析器目前不产生解析器专属统计(架构文档 note 明确说明)。若部署中使用该解析器,需要依赖其他通用指标间接观察解析状况。

七、实践要点小结

  1. 不配置解析器时:macOS 上默认 Apple 解析器(可被 runtime 特性切换),其他平台默认 c-ares;需要精确控制时显式设置typed_dns_resolver_config,它拥有最高优先级。
  2. 超时与重试:记住 Envoy 与 c-ares 库默认值不同(超时 5s/4 次尝试,而非库默认的 2s/3 次);qcache_max_ttl默认 0 表示缓存禁用。
  3. UDP socket 健康:周期性通道刷新(max_udp_channel_duration)与超时即重建(reinit_channel_on_timeout)二者取其一即可,前者适合长期稳定运行,后者适合偶发 socket 故障但会引入通道重建开销。
  4. 可观测性:按解析器选择对应的统计树(dns.cares/dns.apple/dns.hickory);使用 getaddrinfo 时没有专属指标可用。
  5. 平台相关:Apple 解析器仅在 iOS/macOS 构建中可用;Hickory 通过动态模块框架以纯 Rust 运行在独立 Tokio 线程,适合需要 DoT/DoH/DNSSEC 且希望解析不占用事件循环线程的场景。

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询