etcd客户端初始化必须显式传入context.WithTimeout,否则Kratos启动会因无限重试而卡在注册阶段;服务注册需指定唯一ID和/healthz健康端点;Discovery应全局复用单例以防goroutine泄漏;etcd配置需手动接入conf.Load链才生效。

etcd 客户端初始化必须显式传入 context.WithTimeout
直接用 etcdclient.New 创建 client 且不设超时,会导致 Kratos 启动卡在注册阶段——etcd 连接失败时会无限重试,registry.Register 阻塞,整个服务 hang 住。这不是 Kratos 的 bug,而是 etcd client v3 默认行为。
正确做法是包裹一层带超时的 context:
- 用
context.WithTimeout(context.Background(), 5*time.Second)初始化 client - 若连接失败,Kratos 会在 5 秒后报错退出,而不是死等
- 别依赖
grpc.WithBlock()或重试逻辑来兜底,etcd client 本身不自动重试连接建立
服务注册必须指定唯一 ID 和健康检查端点
Kratos 注册到 etcd 的实例 ID 不是自动生成的,如果多个 Pod 使用相同 kratos.Name("user") 且没设 ID,etcd 里会覆盖写入,导致只留一个实例,下游发现永远只看到一个节点。
要确保每个实例可区分:
- 用
kratos.ID(fmt.Sprintf("%s-%s", "user", uuid.NewString()))生成唯一 ID - 显式配置
healthz端点:在bootstrap.yaml中设server.http.health: "/healthz" - etcd 的健康检查依赖这个 HTTP 路径返回 200,否则会被自动剔除
Discovery 客户端复用要小心 goroutine 泄漏
在业务代码里频繁调用 discovery.GetService 时,如果每次都 new 一个 etcdregistry.Discovery 实例,底层 Watcher 会不断新建 goroutine 监听变更,最终 OOM。
正确方式是全局复用单例:
- 在
main.go初始化时创建一次etcdregistry.NewDiscovery(client) - 注入到依赖容器(如 wire)或作为包级变量导出
- 避免在 handler 或 middleware 里临时构造 Discovery 实例
etcd 配置项优先级容易被忽略
Kratos 多源配置中,etcd registry 本身也支持从 etcd 读取配置(比如服务超时、重试策略),但它的加载时机晚于本地 YAML。如果你在 /configs/registry.yaml 里写了 timeout: 3s,又在 etcd 的 /kratos/config/user/timeout 写了 5s,后者不会生效——因为 registry 模块的配置源没加进 conf.Load 链。
要启用 etcd 配置中心能力,必须手动添加:
- 引入
contrib/config/etcd/v2 - 调用
conf.Load(conf.WithSource(&etcdconfig.Source{Client: client, Path: "/kratos/config"})) - 顺序得放在 registry 初始化之后,否则 registry 还没准备好就去拉配置
集群管理不是只把服务注册进去就完事;etcd 的租约续期、watch channel 关闭、key 前缀设计这些细节,一旦漏掉一个,服务就可能“注册了但发现不了”,或者“能发现但连不上”。


















