Post

gRPC Resolver 机制详解

gRPC Resolver 机制详解

gRPC Resolver 机制详解

gRPC 的 Resolver(解析器)机制是其客户端侧负载均衡(Client-side Load Balancing)的核心组件。它的主要职责是将一个服务名称(Target URI)解析为一组后端服务器地址(Addresses)

本文档详细拆解该机制的工作流程、核心组件以及它在本项目(基于 Consul)中的运作方式。

1. 核心流程:从名字到连接

当调用 grpc.NewClient("consul://my-service", ...) 时,gRPC 内部发生如下过程:

  1. 解析 Scheme (Parse) gRPC 首先识别 URI 的前缀(Scheme)。
    • dns:///google.com -> Scheme 是 dns
    • consul://127.0.0.1/my-service -> Scheme 是 consul
    • 如果不带 Scheme(如 localhost:8080),默认使用 passthrough(直连)。
  2. 查找 Resolver (Builder) gRPC 维护一个全局的注册表。它根据 Scheme 找到对应的 Resolver.Builder
    • 本项目通过 import _ "github.com/mbobakov/grpc-consul-resolver" 将 Consul 的 Resolver 注册到了 gRPC 中。
  3. 构建 Resolver (Build) 调用 Builder 的 Build() 方法,创建一个活动的 Resolver 实例。这个实例开始工作(例如发起 DNS 查询或连接 Consul)。

  4. 地址更新 (UpdateState) Resolver 监控目标服务的变化。当发现服务实例列表变更(例如有新 Pod 启动或旧节点下线)时,它会调用 ClientConn.UpdateState() 通知 gRPC 核心层:“当前的后端列表是 [IP1:Port, IP2:Port]”。

  5. 负载均衡 (Balancer) gRPC 拿到最新的地址列表后,交给 Balancer(负载均衡器)。Balancer 决定具体要把 RPC 请求发给哪个连接(SubConn)。

2. 代码层面的核心接口

了解这三个接口有助于理解底层运作:

  • resolver.Builder: 工厂接口。
    • Scheme() string: 返回它支持的协议名(如 “consul”)。
    • Build(target Target, cc ClientConn, opts BuildOptions): 创建解析器。
  • resolver.Resolver: 实际工作的组件。
    • ResolveNow(): 立即强制解析一次(通常在连接断开重试时触发)。
    • Close(): 释放资源。
  • resolver.ClientConn: gRPC 核心层暴露给 Resolver 的回调接口。
    • UpdateState(State): Resolver 用它来更新地址列表和服务配置。

3. 本项目中的运作方式 (Consul Resolver)

在代码中,DiscoverService 返回了类似 consul://127.0.0.1:8500/my-service?healthy=true 的 URI。

gRPC 的处理流程如下:

  1. 初始化: gRPC 识别 consul scheme,调用 github.com/mbobakov/grpc-consul-resolver 的 Builder。

  2. 连接 Consul: Resolver 解析 URI 中的 Host (127.0.0.1:8500) 并连接到 Consul Agent。

  3. Watch (监听): Resolver 不仅仅是“查询一次”,它通常使用 Consul 的 Blocking Query (长轮询) 机制监听 /v1/health/service/my-service 接口。

  4. 动态更新:

    • 扩容场景: 增加一个实例 -> Consul 通知 Resolver -> Resolver 调用 UpdateState 增加地址 -> gRPC 建立新连接并分发流量。
    • 故障场景: 实例宕机(Health Check 失败)-> Consul 通知 Resolver -> Resolver 调用 UpdateState 移除地址 -> gRPC 优雅关闭连接。

4. Resolver 机制的优点

  1. 即时感知: 相比 DNS(受 TTL 影响)或 HTTP 轮询,Resolver 可利用长轮询近乎实时地感知后端变化。
  2. 策略灵活: Resolver 不仅返回地址,还可返回 Service Config(如指定 round_robin 策略或限流配置)。
  3. 解耦: 业务代码只需关注服务名,无需关心底层是 Consul、Etcd、K8s 还是 DNS。

5. 调试与常见问题

  • Scheme 拼写错误: 若未执行 import _ ...,gRPC 会报错 unknown scheme
  • 权限问题: 确保 Resolver 有连接注册中心的权限。
  • 日志调试: 设置环境变量可查看 gRPC 内部 Resolver 日志:
    1
    2
    
    export GRPC_GO_LOG_VERBOSITY_LEVEL=99
    export GRPC_GO_LOG_SEVERITY_LEVEL=info
    

    观察日志中是否包含 ccResolverWrapper: sending update to cc: {Addr: ...}

This post is licensed under CC BY 4.0 by the author.