首页
/ gRPC 名称解析(Name Resolution)全指南:URI 命名语法与 Resolver 插件机制

gRPC 名称解析(Name Resolution)全指南:URI 命名语法与 Resolver 插件机制

2026-09-08 14:19:32作者:郜逊炳

gRPC 客户端在创建 Channel 时,需要把一个"目标名称"(target)解析为真实的服务器地址,这一过程由名称解析系统(Name Resolution)完成。gRPC 以 DNS 为默认名称系统,同时通过一套通用的 URI 语法与可插拔的 Resolver 插件机制,支持 Unix Domain Socket、VSOCK、固定 IP 等多种命名与解析方式。本文基于当前仓库 doc/naming.md,结合 C-core 源码(src/core/resolver/)深度梳理名称语法、默认端口与解析器注册分发机制,帮助你正确构造 Channel target,理解解析结果如何影响负载均衡与服务配置。

概述:gRPC 的目标名称如何被解析

与其他 RPC 框架把"服务名"直接交给路由表不同,gRPC 把"名称解析"设计为 Channel 生命周期中的独立环节。当应用调用诸如 grpc_create_channel("dns:///example.com:443", ...) 之类接口时:

  1. 客户端库解析目标字符串,识别其 URI scheme;
  2. 依据 scheme 从 Resolver 注册表选出对应的 resolver 插件;
  3. 把完整名称交给该插件,插件联系权威源(如 DNS 服务器)完成解析;
  4. 解析结果返回客户端库,用于建连、负载均衡与服务配置下发。

不同语言的 gRPC 客户端库都提供插件机制,允许为不同名称系统接入各自的解析器,因此"名称解析"与具体语言实现解耦,而 C-core 的注册表实现是最直接的参考。

名称语法:基于 RFC 3986 的 URI

用于 gRPC Channel 构造的名称是"自包含的全限定名",采用 RFC 3986 定义的 URI 语法:

  • URI scheme 决定使用哪个 resolver 插件;
  • 未写 scheme 前缀或 scheme 未知,则默认使用 dns scheme;
  • URI path 表示待解析的名称。

大多数 gRPC 实现支持的 scheme 见下表:

Scheme 语法 说明
dns(默认) dns:[//authority/]host[:port] 通过 DNS 解析 host
unix unix:pathunix:///absolute_path Unix Domain Socket(仅 Unix 系统)
unix-abstract unix-abstract:abstract_path 抽象命名空间中的 Unix Socket(仅 Unix 系统)
vsock vsock:cid:port VSOCK(仅 Linux 系统)

以下 scheme 由 gRPC C-core 实现支持,其他语言可能不支持:

Scheme 语法 说明
ipv4 ipv4:address[:port][,address[:port],...] IPv4 地址(逗号分隔可多个)
ipv6 ipv6:address[:port][,address[:port],...] IPv6 地址(逗号分隔可多个)

未来还可能加入 etcd 等更多 scheme。

各 scheme 的细节与默认端口

dns:默认名称系统

dns:[//authority/]host[:port]

  • host 是要通过 DNS 解析的主机;
  • port 是返回给每个地址的端口;若不指定默认 443,但部分实现对于不安全(insecure)Channel 默认使用 80;
  • authority 用于指定 DNS 服务器,仅部分实现支持。在 C-core 中,默认 DNS resolver 不支持该字段;基于 c-ares 的 resolver 支持以 "IP:port" 形式指定(例如 dns://8.8.8.8:53/example.com)。

在 C-core 源码里,地址解析对端口的缺省处理确实落在 443 上,参见 src/core/lib/address_utils/parse_address.ccreturn htons(443); 的默认分支。

unix:Unix Domain Socket

  • unix:pathpath 为套接字位置,可以是相对路径或绝对路径
  • unix:///absolute_path:第二种形式要求绝对路径——URI 中三个斜杠的最后一个斜杠属于路径本身,即 URI 的 path 部分为 /absolute_path

例如目标 unix:///tmp/grpc.sock 实际指向文件系统路径 /tmp/grpc.sock

unix-abstract:抽象命名空间套接字

unix-abstract:abstract_path

  • abstract_path 表示抽象命名空间中的一个名字;
  • 该名字与文件系统路径名毫无关联
  • 套接字不施加任何权限限制,任何进程/用户都可访问;
  • 底层实现要求抽象套接字首字符为 '\0',实现会自动在 abstract_path 前补该空字节,因此不要把空字节写进 abstract_path

vsock:虚拟机通信(仅 Linux)

vsock:cid:port

  • cid 是 32 位上下文标识符(Context Identifier),标识源或目的地(某台虚拟机或宿主机);
  • port 是 32 位端口号,用于区分同一台机器上运行的多个服务。

典型的用法如 vsock:3:50051 表示与 CID 为 3 的虚拟机/宿主机上的 50051 端口通信,适用于宿主机与 guest 之间不经过 IP 栈的场景。

ipv4 / ipv6:固定地址解析(仅 C-core)

  • ipv4:address[:port][,address[:port],...]ipv6:... 支持逗号分隔的多个 address[:port]
  • 不指定端口时默认 443;
  • IPv6 若要与端口连用,地址必须用字面方括号包裹,如 ipv6:[2607:f8b0:400e:c00::ef]:443ipv6:[::]:1234

注意 ipv4/ipv6 scheme 的解析函数同样是缺省 443(见上文 parse_address 的证据)。

从源码看 scheme 分发:Resolver 注册表机制

当目标字符串里没有 scheme 或 scheme 未知时,为什么回落到 DNS?答案在 C-core 的注册表实现 src/core/resolver/resolver_registry.cc

  • Reset()default_prefix 设为 "dns:///"(见 resolver_registry.cc);
  • FindResolverFactory() 先尝试把原目标解析为 URI 并查找对应工厂;找不到时,就用 default_prefix + target 拼接出 dns:///... 再试一次(见 resolver_registry.cc)。这正是"example.com:443 等价于 dns:///example.com:443"的底层来源。

各 scheme 对应的解析器通过工厂(ResolverFactory)注册进 ResolverRegistry,工厂必须提供小写 scheme、URI 合法性校验与实例创建方法。参见 src/core/resolver/resolver_factory.h

内建 Resolver 的注册点

C-core 中 ipv4/ipv6/unix/unix-abstract/vsock 五类地址式解析器统一实现在 src/core/resolver/sockaddr/sockaddr_resolver.cc,注册逻辑 RegisterSockaddrResolver() 显示:

  • IPv4ResolverFactory(scheme 为 ipv4)与 IPv6ResolverFactory(scheme 为 ipv6无条件注册
  • UnixResolverFactoryUnixAbstractResolverFactory 仅在宏 GRPC_HAVE_UNIX_SOCKET 定义时注册;
  • VSockResolverFactory 仅在宏 GRPC_HAVE_VSOCK 定义时注册。

sockaddr_resolver.cc。也就是说,unixunix-abstractvsock 是否可用取决于构建期平台宏,这也与文档中"仅 Unix/Linux 系统"的说明一一对应。

同时,dns scheme 由 src/core/resolver/dns/dns_resolver.h 中的 ClientChannelDNSResolverFactory 提供,其内部再根据构建选项与运行环境在 c-ares 与 native 两种实现间选择(见下文)。

ParseUri() 解析时还会拒绝带 authority 的 URI(authority-based URIs not supported...),并逐个按逗号切分地址构造 EndpointAddressesList(见 sockaddr_resolver.cc)。这与文档中 ipv4/ipv6 支持逗号分隔多地址的描述吻合。

Resolver 插件的职责:返回什么给客户端

客户端库依据 scheme 选中 resolver 插件后,把全限定名称字符串交给它。Resolvers 需要能够联系 authority 并把解析结果返回客户端库,返回内容包含:

  • 解析出的地址列表:每个地址都是(IP, port)二元组,且可附带一组任意的键值对属性(attributes),用于把解析器获知的信息传达给负载均衡策略
  • 一份服务配置(service config):例如重试参数、超时、负载均衡策略名称等,解析结果中可选携带。

此外,插件 API 允许 resolver 持续监听一个端点(endpoint),一旦解析结果变化即返回更新后的解析结果。这意味着解析不是"一次性的静态查询",而可能是一次持续到 Channel 关闭的异步观察过程——这也是客户端库实现服务发现变更后自动切换地址的基础。

从 C-core 的 SockaddrResolver 可以看到这类"静态"解析器的骨架:构造时把解析好的地址列表保存下来,StartLocked() 只调用一次 result_handler_->ReportResult() 上报结果(见 sockaddr_resolver.cc);而对 DNS 这类可变的端点,解析器实现则会周期性刷新并把更新推给 Channel。

平台差异与运行期环境变量

DNS 解析器后端:ares 还是 native?

在 C-core 中,dns scheme 的实际解析后端可通过环境变量 GRPC_DNS_RESOLVER 选择(完整说明见 doc/environment_variables.md):

  • ares:基于 c-ares 库的解析器,多数平台默认(iOS、Android、Node 除外);只有 c-ares 支持在 URI 中指定 DNS 服务器 authority(IP:port);
  • native:基于 getaddrinfo() 的解析器,为执行名称解析会创建新线程。

NetBIOS 注意事项:如果网络依赖 NetBIOS 或 DNS 与 NetBIOS 混合解析(例如某些 Windows 网络),应使用 native 解析器,或确保所有 NetBIOS 名也在 DNS 中配置——因为 ares 只做纯 DNS 名称解析。若 gRPC 构建时未启用 c-ares,则该环境变量被忽略。

端口默认值差异

  • dnsipv4ipv6 缺省端口均为 443(部分实现 insecure 通道用 80);
  • 实践中若以明文方式快速联调,常显式书写端口,例如 dns:///localhost:50051

实战:构造 Channel 目标字符串

综合上文,各类目标的典型写法可归纳如下(在任意语言 gRPC API 的 Channel target 参数中直接使用):

# DNS:主机名(等价写法)
example.com:443
dns:///example.com
dns:///example.com:443

# DNS:指定 DNS 服务器(c-ares 后端,C-core)
dns://8.8.8.8:53/example.com

# Unix Domain Socket:相对/绝对路径
unix:/tmp/grpc.sock
unix:///tmp/grpc.sock

# 抽象命名空间 Unix Socket(首字符空字节由实现自动补上)
unix-abstract:my_abstract_sock_name

# VSOCK(仅 Linux)
vsock:3:50051

# 固定地址(仅 C-core,可逗号分隔多地址,端口缺省 443)
ipv4:127.0.0.1:50051
ipv4:192.0.2.1:443,192.0.2.2:443
ipv6:[::1]:50051
ipv6:[2607:f8b0:400e:c00::ef]:443

这些字符串在仓库测试中也被直接用作 target 证据,例如 C-core 的 Channel 相关单元测试以 ipv4:127.0.0.1:1234 形式构造解析成功的地址结果(见 test/core/client_channel/client_channel_test.cc)。

选择建议:

  • 服务端拥有标准域名、需要多地址轮换/故障转移时,使用 dns:///host[:port],让其走完整的 resolver + 负载均衡链路;
  • 单机进程间通信追求低延迟与免 TCP 时,用 unix: / unix:///
  • 容器/宿主机强隔离或绕过网络栈的虚拟化场景,用 vsock:cid:port
  • 调试或测试阶段希望完全绕开 DNS、锁定具体端点时,用 ipv4: / ipv6:

小结

gRPC 名称解析把"channel 目标"统一建模为 RFC 3986 URI:scheme 决定解析器、path 决定要解析的名字、缺省端口 443、缺省 scheme 回落 DNS。C-core 通过 ResolverRegistry + 各 ResolverFactory(DNS、Sockaddr 系列)把这一抽象落地为可插拔、可编译期裁剪的插件体系,解析结果(地址列表 + 属性 + service config)既可支持负载均衡策略,也能持续监听动态刷新。理解这套语法与机制,是正确配置 gRPC 客户端、排查连接失败与实现自定义名称系统的前提。

延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391