Consul客户端需先启动Agent再初始化,地址配置应显式构造;服务注册必需ID和Name,Address须填可访问IP;发现失败主因是健康检查未通过、未设PassingOnly或DC错误;注销须监听信号手动调用ServiceDeregister。
必须先确认本地或远程 Consul Agent 已启动(consul agent -dev 或生产模式),否则 api.NewClient() 会静默失败或超时。默认连接 http://127.0.0.1:8500,若地址不同,需显式传入配置:
cfg := api.DefaultConfig()
cfg.Address = "192.168.1.100:8500"
client, err := api.NewClient(cfg)
if err != nil {
log.Fatal(err)
}
注意:不建议在 DefaultConfig() 后直接修改字段(如 cfg.Address = ...),应新建配置对象或用 api.Config{Address: ...} 显式构造,避免隐式依赖默认行为。
调用 client.Agent().ServiceRegister() 时,ID 和 Name 是强制字段;Address 和 Port 若为空,Consul 会尝试从本地网络接口推断,但多网卡或 Docker 环境下极易注册为 127.0.0.1,导致其他服务无法访问。
ID 必须全局唯一,建议包含服务名+主机名+端口,例如 "user-srv-host1-8080"
Name 是服务逻辑名,用于发现,例如 "user-service"
Address 应填可被其他服务直连的 IP(非 localhost),K8s 中常用 status.hostIP 或 Downward APICheck 建议至少配置一个 HTTP 健康检查,否则服务上线即视为健康,掩盖真实故障reg := &api.AgentServiceRegistration{
ID: "order-srv-node1-9001",
Name: "order-service",
Address: "10.0.2.15",
Port: 9001,
Check: &api.AgentServiceCheck{
HTTP: "http://10.0.2.15:9001/health",
Timeout: "2s",
Interval: "5s",
DeregisterCriticalServiceAfter: "30s",
},
}
err := client.Agent().ServiceRegister(reg)
调用 client.Health().Service() 返回空列表,常见原因不是代码写错,而是环境或语义理解偏差:
passing,或执行 curl http://localhost:8500/v1/health/service/order-service
PassingOnly: true:默认返回所有状态实例,包括 critical 或 warning,需显式过滤
调用需指定 dc 参数,否则只查本 DCres, _, err := client.Health().Service("user-service", "", false, &api.QueryOptions{
PassingOnly: true,
Datacenter: "dc1",
})
if err != nil {
log.Fatal(err)
}
for _, srv := range res {
fmt.Printf("Addr: %s:%d\n", srv.Service.Address, srv.Service.Port)
}
进程退出时不注销,会导致 Consul 中残留“僵尸服务”,影响负载均衡和故障转移。不能只靠 TTL 自动剔除——它有延迟,且无法及时释放资源。
正确做法是在 os.Interrupt 或 syscall.SIGTERM 信号处理中调用 ServiceDeregister:
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
go func() {
<-quit
client.Agent().ServiceDeregister("order-srv-node1-9001")
os.Exit(0)
}()
更稳妥的方式是结合 context.WithTimeout 防止注销卡住,尤其在 Agent 不可达时。另外,K8s 环境中还需配合 preStop hook,确保容器终止前注销完成。