Gin 不处理负载均衡,真正起作用的是 grpc.Dial 配置与 Consul 服务发现;必须使用 consul:// 协议、显式指定 loadBalancingPolicy、注册带健康检查的服务,并复用全局 gRPC 连接。

直接结论:Gin 本身不处理负载均衡,真正起作用的是 gRPC 客户端连接时的 grpc.Dial 配置 + Consul 服务发现协议,Gin 只负责发起调用、透传请求。
gRPC Dial 必须用 consul:// 协议格式
这是整个集成能动起来的前提。gRPC 官方不原生支持 Consul,但通过第三方 resolver(如 grpc-consul-resolver)或直接使用 Consul 的 DNS 接口+内置 resolver,可让 grpc.Dial 解析 consul:// 地址并自动拉取健康实例列表。
- 正确写法:
consul://127.0.0.1:8500/user-service?wait=10s—— 注意协议头、端口、服务名、query 参数都必须匹配 Consul 实际配置 - 错误写法:
127.0.0.1:8500或http://...,这类地址会被 gRPC 当作普通 IP 直连,完全绕过服务发现 - 如果 Consul 启用了 ACL 或 TLS,
grpc.Dial还需额外传入grpc.WithTransportCredentials,否则 dial 失败且错误信息模糊(常见报错:connection refused或failed to resolve address)
loadBalancingPolicy 必须显式指定
gRPC 默认不启用任何负载均衡策略,即使你连的是 consul:// 地址,客户端仍可能只打到第一个解析出的实例上(尤其在非轮询场景下)。
- 必须加:
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`) - 其他可用策略:
pick_first(默认,单点)、weighted_round_robin(需额外插件)、grpclb(需独立 LB 服务) - 注意 JSON 字符串必须是合法格式,引号要转义;写成
{"loadBalancingPolicy": 'round_robin'}会静默失败,连接卡住无日志
Consul 服务注册必须带健康检查
Consul 的服务发现能力依赖于健康状态。没有健康检查的服务条目,即使注册成功,gRPC resolver 也可能跳过它或持续尝试已下线节点。
- 注册时至少配置
Check.HTTP和Check.Interval,例如:HTTP: "http://localhost:8000/health" - Gin 服务自身要暴露
/health路由,并返回 HTTP 200;返回 5xx 或超时会导致 Consul 将该实例标记为critical - 不要依赖 Consul UI 上“服务存在”就认为可用——务必 curl 检查
/v1/health/service/{name}?passingAPI 返回是否含真实健康实例
连接复用与全局 Client 存储很关键
每次 HTTP 请求都新建 grpc.Dial 会触发重复服务发现、DNS 解析、连接建立,性能差且易触发 Consul 限流。
- 必须在 Gin 启动时一次性调用
InitSrvConn类函数完成grpc.Dial,并将*grpc.ClientConn和生成的 proto client(如proto.NewUserClient)存入全局变量或依赖注入容器 - 禁止在路由 handler 中写
conn, _ := grpc.Dial(...)—— 这会导致连接泄漏、fd 耗尽、服务发现风暴 - 生产环境建议加
grpc.WithBlock()阻塞等待首次连接就绪,避免 Gin 启动后立即收到请求却因连接未建好而 panic
最容易被忽略的是 resolver 的加载时机和健康状态同步延迟:Consul 更新实例后,gRPC resolver 不是实时感知的,通常有几秒到十几秒的缓存窗口。如果你在滚动发布时看到部分请求失败,先确认不是这个“最终一致性”窗口导致的,而不是急着改代码。











