hyperf的grpc客户端从v3.1起原生支持多客户端负载均衡,但需同时满足三个前提:启用服务发现(enable.discovery=true)、服务接口契约完全一致、注册中心中多个实例健康状态为up;否则默认仅调用首个实例。

Hyperf 的 gRPC 客户端从 v3.1 开始原生支持多客户端负载均衡,但默认不启用——你必须显式配置 load_balancer 且服务发现已就绪,否则所有请求会打到第一个可用实例上(不是轮询,更不是随机)。
gRPC 客户端 load_balancer 配置生效的三个前提
很多团队配了 load_balancer => 'roundrobin' 却没效果,根本原因是漏掉以下任一条件:
- 服务消费者必须启用服务发现:
enable.discovery => true,不能只靠静态servers数组 -
service接口必须与服务提供方注册的契约完全一致(包括命名空间、方法签名、返回类型),否则ServiceGovernance不会将其纳入负载列表 - 注册中心(如 Nacos/Consul)中该服务的多个实例必须处于
UP状态且健康检查通过;Hyperf 不会主动剔除失联节点,除非你启用了hyperf/health-check并配置了心跳上报
Random / RoundRobin / LeastConn 算法的实际行为差异
不同算法在协程环境下的表现和适用场景差别很大,不是简单“换名字”就能切换:
-
Random:每次调用前从当前健康实例列表中随机选一个,适合实例性能均一、无状态的场景;但单个协程内多次调用可能命中同一实例(因实例列表未实时刷新) -
RoundRobin:按顺序轮询,但不是全局计数器——每个BaseClient实例维护自己的索引,所以多个协程并发调用时,实际分布接近随机 -
LeastConn:依赖hyperf/load-balancer的连接统计,仅对长连接有效;它统计的是当前客户端到各后端的活跃连接数(非请求并发数),在突发流量下反应滞后,更适合稳定长连接场景
为什么 gRPC 连接复用会让负载看起来“不均衡”
这是最常被误判的问题:监控看到某台后端 CPU 飙高,其他几台空闲,并不一定是负载均衡失效,而是 gRPC 的 HTTP/2 连接复用特性导致的:
- Hyperf 的
GrpcClient\BaseClient默认复用GrpcChannel,一个 Channel 对应一个 TCP 连接 - 即使配置了
RoundRobin,只要 Channel 没重建,后续请求仍走原连接(因为连接是粘性的) - 解决办法不是关复用,而是控制 Channel 生命周期:在
config/autoload/services.php中为消费者设置'pool' => ['max_connections' => 8],并确保业务侧不长期持有单一 client 实例
跨 VPC 或混合云部署时的 DNS 解析陷阱
当 Hyperf 节点和 gRPC 后端不在同一 VPC,或使用公网域名注册时,dns 解析策略容易出问题:
- Hyperf 默认不内置
DnsResolverFactory,若你在GrpcChannel::forAddress('dns:///api.example.com')中硬编码 dns 方案,会直接报错Unknown resolver scheme: dns - 正确做法是引入
hyperf/grpc-client的扩展支持,或改用static方案 + 主动拉取 DNS 记录后写入服务列表 - 更稳妥的方式是绕过 DNS:让 Consul/Nacos 返回 IP+Port,客户端直连,避免中间解析延迟和 TTL 缓存导致的实例列表陈旧
真正决定负载是否均衡的,从来不是算法名称,而是 Channel 复用粒度、服务发现时效性、以及你有没有在协程生命周期里正确释放 client 实例——这些细节在文档里往往一笔带过,但线上压测时全会暴露出来。











