Kratos 必须显式注册 discovery.Builder 到 gRPC resolver 并使用 discovery:///service-name 格式 endpoint,否则报“scheme not registered”;需提前初始化 Consul/Nacos 客户端,显式指定 balancer 名称如 "round_robin",并复用 ClientConn 避免资源泄漏。

Kratos 默认不自动启用服务发现,必须显式配置 discovery 实例并注入到 grpc.DialContext 或全局 dial 选项中,否则即使写了 "discovery:///service-name" 也会报 resolver: scheme "discovery" not registered 错误。
注册 discovery 实例到 gRPC resolver 体系
Kratos 的服务发现能力依赖于将自定义 discovery.Builder 注册进 gRPC 的 resolver 系统。这一步不能省略,且必须在调用 grpc.DialContext 前完成。
- 使用
grpc.WithDiscovery(r)是最简方式,其中r是实现了registry.Discovery接口的实例(如consul.New(client)或nacos.New(client)) - 若需复用同一
discovery实例给多个 client,推荐通过grpc.WithDefaultCallOptions或全局globalDialOptions注入,避免每个 dial 都重复传参 - 未注册时直接 dial
"discovery:///xxx"会 panic:错误信息为panic: resolver: scheme "discovery" not registered,不是配置地址写错,而是 resolver 没加载
clientOptions.endpoint 必须是 discovery:/// 形式
服务发现模式下,endpoint 字符串必须以 "discovery:///" 开头,后面跟服务名(即注册时的 ServiceInstance.Name),不能带端口或协议前缀。
- ✅ 正确:
"discovery:///user-service"、"discovery:///order-service" - ❌ 错误:
"discovery://user-service:9000"(协议错误)、"user-service"(无 scheme,走默认 passthrough resolver)、"http://user-service"(gRPC 不识别) - 服务名需与注册时完全一致,包括大小写和连字符;Kratos 不做 normalize,Consul/Nacos 中注册的是
user-service,这里就不能写成UserService
Consul/Nacos 等注册中心客户端必须提前初始化
registry.Discovery 实现不是开箱即用的,需要先构造底层注册中心客户端(如 Consul API client 或 Nacos naming client),再包装成 Kratos 的 Discovery 实例。
- Consul 示例:需先调用
api.NewClient(&api.Config{Address: "127.0.0.1:8500"}),再传给consul.New() - Nacos 示例:需构造
clients.NacosClientParam,含ServerConfigs和ClientConfig,再调用clients.NewNamingClient(),最后 wrap 成nacos.New() - 常见坑:忘记设置
NamespaceId(Nacos)或scheme(Consul),导致连接后 fetch 实例为空;Consul 的scheme默认是http,若启用了 TLS 就必须显式设为https
负载均衡策略必须显式指定且匹配内置名称
Kratos 不会自动 fallback 到默认 balancer;如果没传 grpc.WithBalancerName,gRPC 会使用 pick_first(只选第一个节点),失去负载均衡意义。
- 可用策略包括:
"round_robin"、"wrr"(加权轮询)、"random",名称必须全小写、严格匹配,"WRR"或"weighted_rr"会静默失败 -
wrr依赖服务注册时设置的InitialWeight字段(通过ServiceInstance.Metadata["weight"]或实现Node.InitialWeight()接口) - 若使用自定义 selector,需确保它已通过
selector.Register注册,否则WithBalancerName会 panic 报balancer is not registered
真正容易被忽略的是 resolver 的生命周期管理——discovery.Builder 创建的 resolver.Resolver 会持续 Watch 注册中心变更,但如果你在测试中反复 Dial 又不 Close conn,可能堆积大量 goroutine 和 HTTP long-poll 连接。生产环境务必复用 *grpc.ClientConn,而不是每次请求都新建。











