资讯动态

gRPC DNS 名称解析实现解析:从 Resolver 接口到 c-ares/EventEngine/原生三种实现

发布时间:2026/9/10 15:06:44 来源:尧图企业网站定制
gRPC DNS 名称解析实现解析从 Resolver 接口到 c-ares/EventEngine/原生三种实现【免费下载链接】grpcC based gRPC (C, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc导读在 gRPC 中客户端通道ClientChannel通过名称解析器Resolver把用户传入的dns:///host:port形式的 URI 转换成一组真实可连接的后端端点地址。本文以仓库中 src/core/resolver/dns/AGENTS.md 为核心骨架结合 dns_resolver.cc、polling_resolver.cc、ares_resolver.cc 等源码系统讲解 DNS 解析在 gRPC 中的定位、三种可选解析实现c-ares / EventEngine / 原生、核心配置参数、SRV/TXT 扩展查询机制以及如何切换解析器。读完本文你将掌握 gRPC DNS 解析的完整调用链、关键 Channel Arg 与GRPC_DNS_RESOLVER环境变量的作用并能在自己的应用中做出正确的解析器选型。gRPC 名称解析DNS 模块在整个链路中的位置gRPC 采用URI 名称 Resolver 插件的架构用户在创建通道时传入形如dns:///example.com:443的 URI核心层通过 resolver_registry.cc 中注册的工厂ResolverFactory按 URI scheme 找到对应实现。DNS 模块正是dnsscheme 的落地实现其职责在 src/core/resolver/dns/AGENTS.md 中被明确为提供Resolver接口的一个具体实现用 DNS 完成名称解析。该模块位于 src/core/resolver/dns/核心文件包括dns_resolver.h定义ClientChannelDNSResolverFactoryscheme()返回dns与集中式注册入口RegisterDnsResolverdns_resolver.ccClientChannelDNSResolver的主体实现继承自PollingResolverservice_config_helper.cc解析 TXT 记录中grpc_config服务配置 JSON 的选择逻辑。从 dns_resolver.cc 可以看到DNS resolver 通过CoreConfiguration::Builder注册进解析器注册表成为 gRPC 默认的名称解析机制void RegisterDnsResolver(CoreConfiguration::Builder* builder) { VLOG(2) Using EventEngine dns resolver; builder-resolver_registry()-RegisterResolverFactory( std::make_uniqueClientChannelDNSResolverFactory()); }事实依据AGENTS.md 明确说明这是 gRPC 的默认名称解析机制经过多年生产环境验证。三种 DNS 解析实现c-ares / EventEngine / 原生AGENTS.md 用三个子目录概括了解析实现的三种方案结合当前仓库源码结构三者对应关系如下实现说明当前仓库中的落点c-ares基于 c-ares 异步库不阻塞线程可配置查询超时ares_resolver.cc、ares_resolver.h依赖 third_party/cares 的 c-ares 头文件EventEngine使用 gRPC 事件引擎EventEngine内置的 DNS 解析能力与平台 IO 引擎解耦dns_resolver.cc 中EventEngineDNSRequestWrapper通过EventEngine::DNSResolver发起查询native原生直接使用操作系统的原生 DNS 解析器如getaddrinfo风格由各平台 EventEngine 实现提供如 posix_engine.cc、windows_engine.cc 中的 resolver 实现需要说明的是在当前仓库版本中DNS 模块源码树只有 dns_resolver.cc 一个统一实现ClientChannelDNSResolverc-ares 与原生两种能力被收敛到 EventEngine 抽象之下——StartRequest()调用event_engine_-GetDNSResolver(...)拿到解析器而EventEngine::DNSResolver的具体后端c-ares 或平台原生由 EventEngine 实现决定。AGENTS.md 中的c_ares/、event_engine/、native/三个子目录反映的是该模块历史演进中的目录组织形态今天的代码已统一经由 EventEngine 接口调度。从源码结构可以推断选哪个解析器实际上分两层——外层是GRPC_DNS_RESOLVER环境变量决定 EventEngine 使用 c-ares 后端还是原生后端内层是dnsscheme 统一注册ClientChannelDNSResolverFactory。核心实现ClientChannelDNSResolver 的完整解析流程ClientChannelDNSResolver继承自 polling_resolver.h 中的PollingResolver具备周期性轮询能力min_time_between_resolutions控制两次解析的最小间隔默认 30 秒见 dns_resolver.cc。每次解析由StartRequest()触发创建EventEngineDNSRequestWrapper并并行发起三类 DNS 查询见 dns_resolver.ccA/AAAA 主机名查询LookupHostname解析目标主机名默认端口为https443SRV 记录查询若开启GRPC_ARG_DNS_ENABLE_SRV_QUERIES查询_grpclb._tcp.name用于 gRPC-LBgrpclb负载均衡器的发现TXT 记录查询若未禁用服务配置解析查询_grpc_config.name用于从 DNS 直接下发 service config。查询全部完成后OnResolvedLocked()汇总结果dns_resolver.cc若主机名地址与 balancer 地址均为空返回UNAVAILABLE错误如No results from DNS queries若至少有一类地址则依次填充普通端点地址、TXT 解析出的服务配置、SRV 解析出的 balancer 地址部分失败仅写入resolution_note不阻断整体解析。// 关键配置读取dns_resolver.cc 构造函数 request_service_config_ !channel_args() .GetBool(GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION).value_or(true); enable_srv_queries_ channel_args() .GetBool(GRPC_ARG_DNS_ENABLE_SRV_QUERIES).value_or(false); query_timeout_ms_ std::chrono::milliseconds(std::max(0, channel_args() .GetInt(GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS) .value_or(GRPC_DNS_DEFAULT_QUERY_TIMEOUT_MS))); // 默认 120000ms超时与重试由 backoff.h 的BackOff策略支持DNS 模块的默认参数定义在 dns_resolver.cc参数默认值初始连接退避GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS1 秒重连退避倍数GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER1.6最大退避GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS120 秒抖动GRPC_DNS_RECONNECT_JITTER0.2查询超时GRPC_DNS_DEFAULT_QUERY_TIMEOUT_MS120000 毫秒DNS 相关的 Channel Arg 与环境变量Channel Arg通道参数在 include/grpc/impl/channel_arg_names.h 中定义了 DNS 模块的全部通道参数可在创建通道时通过channel_args设置Channel Arg 名称说明默认值grpc.dns_enable_srv_queriesGRPC_ARG_DNS_ENABLE_SRV_QUERIES是否查询_grpclb._tcp.SRV 记录以发现 grpclb 负载均衡器falsegrpc.dns_ares_query_timeoutGRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS单次 DNS 查询超时毫秒0表示不超时120000grpc.dns_min_time_between_resolutions_msGRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS两次主动解析之间的最小间隔30000毫秒GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION置为true时禁用通过 DNS TXT 记录解析 service configfalse即默认启用 TXT 服务配置解析注意GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS的命名保留历史语义源自 c-ares 时代但当前实现将其复用于 EventEngine 后端见 dns_resolver.cc 中的 TODO 注释。环境变量GRPC_DNS_RESOLVER在 src/core/config/config_vars.yaml 与 config_vars.cc 中定义了环境变量GRPC_DNS_RESOLVERDeclares which DNS resolver to use. The default is ares if gRPC is built with c-ares support. Otherwise, the value of this environment variable is ignored.即若 gRPC 编译时带上了 c-ares 支持默认使用 c-ares 解析器否则该环境变量的取值被忽略回退到 EventEngine 原生实现。这一机制说明解析器选型同时受构建时特性与运行时环境变量双重控制。SRV 记录grpclb 负载均衡器发现当grpc.dns_enable_srv_queries开启后解析器查询_grpclb._tcp.target的 SRV 记录dns_resolver.cc。SRV 返回的每个条目都包含主机与端口随后为每个 balancer 主机再发起一次 A/AAAA 查询OnSRVResolved→OnBalancerHostnamesResolved见 dns_resolver.cc。值得注意的实现细节balancer 地址会附带GRPC_ARG_DEFAULT_AUTHORITY通道参数取 SRV 记录中的主机名保证后续 RPC 的 authority 正确dns_resolver.cc若 SRV 查询超时timeout_handle_已被清空后续 balancer 主机名查询不再发起并记录错误timed out - not initiating subsequent balancer hostname requestsbalancer 地址通过SetGrpcLbBalancerAddresses注入到解析结果供 grpclb 负载均衡策略使用dns_resolver.cc。TXT 记录通过 DNS 下发 Service Config若未禁用解析器会查询_grpc_config.target的 TXT 记录并寻找以grpc_config为前缀的条目dns_resolver.cc。找到后JSON 内容交由 service_config_helper.cc 的ChooseServiceConfig处理解析为 JSON 数组每项为一个服务配置选择ServiceConfigChoice字段包括clientLanguage、percentage、clientHostname、serviceConfig依次匹配语言必须是c若指定、当前主机名必须在clientHostname列表内若指定、随机百分比rand() % 100必须落在percentage内若指定命中第一个匹配项即返回其serviceConfigJSON否则返回空串表示无服务配置。// service_config_helper.cc 中的选择判定 if (!choice.client_language.empty() !vector_contains(choice.client_language, c)) continue; if (!choice.client_hostname.empty() !vector_contains(choice.client_hostname, grpc_gethostname())) continue; if (choice.percentage ! -1) { int random_pct rand() % 100; if (random_pct choice.percentage || choice.percentage 0) continue; }这种机制使运维人员可以在不修改客户端代码的前提下通过 DNS TXT 记录灰度下发负载均衡策略、重试配置等 service config。TXT 解析失败时只要主机名地址解析成功错误仅记录在resolution_note中不会阻断连接见MaybePopulateServiceConfigLocked的注释dns_resolver.cc。解析器选型对性能的影响与建议AGENTS.md 在 Notes 中特别强调选择哪个 DNS 解析器会对性能产生显著影响为你的应用选对解析器很重要。结合上文可以给出如下选型参考c-ares 后端异步、非阻塞适合高并发、大量 channel 的服务器场景依赖第三方库 third_party/cares需在构建时启用且支持grpc.dns_ares_query_timeout超时控制EventEngine 原生后端直接利用操作系统解析器部署简单、无额外依赖适合对解析吞吐要求不高的客户端场景两者都通过EventEngine::DNSResolver统一接口接入业务代码无需区分。从代码层面看ClientChannelDNSResolver::StartRequest()通过event_engine_-GetDNSResolver({/*dns_server*/authority()})获取解析器dns_resolver.cc解析器创建失败时会把错误同时写入addresses与service_config并立即完成请求。此外IsValidUri会拒绝没有 server name 的dns:URIdns_resolver.cc保证dns:///后必须携带目标主机。进一步阅读解析器抽象与注册机制resolver.h、resolver_registry.cc轮询式解析器基类polling_resolver.ccEventEngine DNS 接口与 c-ares 后端ares_resolver.h、ares_resolver.cc通道参数定义channel_arg_names.h环境变量定义config_vars.yaml相关测试可参考 test/core/resolver 目录下的解析器测试用例【免费下载链接】grpcC based gRPC (C, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价