kratos.New() 启动 panic 的根本原因有三:未在 main() 开头调用 conf.Load() 注册配置源、未创建 http/grpc.Server 实例、未将实例传入 kratos.New() 的 Server 参数列表。

Consul 客户端初始化必须检查返回值,否则 kratos.New() 会 panic
kratos.New() 不做 nil 防御,传入 nil 的 registry.Registry 会导致启动时直接 panic:「invalid memory address」。这不是配置错误,而是构造失败后忽略了返回值。
常见错误写法:
reg, _ := consul.New(&consul.Config{Address: "127.0.0.1:8500"}) // ❌ 忽略 error,reg 可能为 nil
app := kratos.New(kratos.Registrar(reg), ...) // panic!
正确做法是显式判断:
-
consul.New()返回*consul.Registry和error,二者都必须检查 - 若
error != nil,*consul.Registry一定为nil(官方实现保证) - 建议在
wire.Build()提供的 provider 中完成构造和校验,避免逻辑泄漏到main()
服务发现模式的 endpoint 必须用 discovery:///xxx 格式
gRPC Dial 时启用服务发现,endpoint 不能写 IP+端口,也不能写 consul://... —— Kratos 要求统一使用 discovery:///service-name 形式。
例如调用用户服务:
conn, err := grpc.DialContext(ctx, "discovery:///user-service",
grpc.WithDiscovery(r), // r 是 registry.Discovery 实例
grpc.WithBalancerName("wrr"),
)
关键点:
- 协议头固定为
discovery://,后面跟三斜杠///,再跟服务名(即注册时ServiceInstance.Name) - 服务名需与注册端完全一致,区分大小写;常见坑是注册用了
"UserService",而发现写了"user-service" - 如果 endpoint 写成
"127.0.0.1:9000"或"localhost:9000",gRPC 会跳过服务发现逻辑,直连该地址
resolver.Register() 是全局单例,重复调用会覆盖且不报错
resolver.Register() 本质是往 gRPC 的 resolver map 里塞一个 key-value,key 是 scheme(如 "discovery"),value 是 builder。多次调用同 scheme 的 register,后者会静默覆盖前者。
典型误用场景:
- 在多个 package 的
init()函数中各自调用resolver.Register(dis) - 测试时每个 test case 都 new 一个
consul.Registry并 register,导致实际生效的只有一个 - 混合接入 Consul 和 Nacos,在同一进程里先后调用两次
resolver.Register()
正确姿势:
- 只在应用初始化早期(如
main()开头或 wire provider 中)调用一次 - 确保所有服务发现客户端共用同一个
registry.Discovery实例 - 验证是否生效:打印
grpc.GetResolver("discovery") != nil
Consul 健康检查路径未配会导致服务状态为 critical
Consul 默认对注册的服务执行健康检查,若未配置或路径不可达,实例状态会变成 critical,Kratos 客户端默认**过滤掉 critical 状态的实例**,导致服务发现列表为空、请求全部失败。
解决方法取决于 Consul 客户端构造方式:
- 使用
contrib/registry/consul包时,在consul.New()后手动设置健康检查:reg.SetCheck(&api.AgentServiceCheck{HTTP: "http://127.0.0.1:8000/health", Interval: "5s"}) - 若通过
api.Client自建,注册ServiceInstance时需填充Checks字段 - 开发期可临时关闭健康检查(仅限本地调试):
client, _ := api.NewClient(&api.Config{Address: "...", Scheme: "http"}); reg := consul.New(client); reg.WithCheck(false)
最易忽略的一点:健康检查 URL 必须能被 Consul Server 访问到,不是本机 localhost —— 若 Consul 运行在 Docker 或远程节点,需填容器内网 IP 或 host.docker.internal。


















