Consul注册器必须先用api.DefaultConfig()显式设置Address等参数初始化api.Client,再传入consul.New()创建;若仅传地址字符串或client初始化失败,会导致注册器为nil、服务无法注册或健康检查失败。

Consul 能用,但必须手动初始化 client 并传给 consul.New(),不能直接把地址字符串塞进去;否则 consul.New() 返回 nil + error,而很多人只检查 error,结果把 nil 注册器传给 kratos.New(),服务启动后根本不注册——连健康检查都看不到。
怎么创建合法的 Consul 注册器实例
核心是先构造 api.Client,再喂给 consul.New()。Kratos 的 contrib/registry/consul 不接受地址字符串,也不自动 new client。
-
api.DefaultConfig()必须显式设Address,Docker 环境别写localhost,改用宿主机 IP 或host.docker.internal - 如果 Consul 启用了 ACL,需在 config 中补
Token字段,否则Register()会静默失败(返回nil实例 +rpc error: code = Unknown desc = Permission denied) - 不要复用全局
api.Client实例做多个 registry ——consul.New()内部会基于 client 做封装,但并发注册时若 client 被意外关闭,会导致所有注册器失效
config := api.DefaultConfig()
config.Address = "127.0.0.1:8500"
client, err := api.NewClient(config)
if err != nil {
log.Fatal(err)
}
reg := consul.New(client) // ← 这才是合法实例
注册时 ServiceInstance 的 Endpoints 格式要对
Kratos 的 ServiceInstance.Endpoints 是字符串切片,但 Consul 注册器只认 grpc://ip:port 或 http://ip:port 这类带 scheme 的格式;填 "127.0.0.1:9000" 会被忽略,导致服务注册成功但无法被发现。
- gRPC 服务必须写
"grpc://" + ip + ":" + port,HTTP 服务对应写"http://" - IP 不能用
0.0.0.0,得是实际可路由的网卡地址(os.Hostname()+net.LookupIP()获取更稳妥) -
ID字段建议包含主机名+端口+时间戳,避免同机器多实例冲突;Consul 本身不校验重复 ID,但 deregister 时会误删其他实例
为什么服务注册了却查不到?检查 Watcher 初始化时机
Consul 的服务发现依赖 resolver.Register() 全局注册一个 naming.Resolver,这个动作必须在任何 gRPC dial 之前完成;否则 grpc.Dial("discovery:///user.service") 会 fallback 到 DNS 解析,报 failed to resolve address。
- 务必在
main()开头、甚至init()里调用resolver.Register(dis),别等到 server 启动后才注册 - 同一个进程里不能多次调用
resolver.Register()不同实现(比如同时注册 etcd 和 consul),后者会覆盖前者,导致部分服务发现失效 - 测试时用
client.Health().Service("service-name", "", true, nil)直接查 Consul API,绕过 Kratos 层,能快速区分是注册问题还是发现链路问题
Consul 健康检查失败的典型表现和修复点
注册成功不代表健康检查通过。Consul 默认对注册的服务发起 HTTP GET /health,如果服务没暴露该 endpoint 或返回非 2xx,实例状态会变成 critical,被自动从服务列表剔除。
- Kratos 默认不内置健康检查 endpoint,需自己加一个 HTTP server 处理
/health并返回200 OK - 若用 gRPC 服务,Consul 无法直接探活,得配 TCP 检查(在
ServiceInstance.Metadata里加"consul-check-type": "tcp"),否则 Consul 一直认为服务不可用 - 本地开发时关掉 Consul 的健康检查(
-dev -disable-host-node-id启动参数)能跳过这步,但上线前必须打开并验证
最易被忽略的是:Consul client 初始化失败时返回的 error 很模糊,比如网络不通可能报 Get "http://127.0.0.1:8500/v1/status/leader": dial tcp 127.0.0.1:8500: connect: connection refused,但 consul.New() 的 error 只是 nil;必须先确保 client 可用,再构建 registry。


















