Consul 多数据中心服务发现必须显式指定 Datacenter,否则 client.Health().Service() 默认只查本地 DC;跨 DC 调用需手动传入 api.QueryOptions{Datacenter: "xxx"},且服务注册 Address 必须为 remote DC 可达 IP,ACL Token 需全局同步,WAN Gossip 断开将导致服务发现退化为局部视图。

Consul 多数据中心配置必须显式指定 Datacenter 字段
默认情况下,client.Health().Service() 只查本地数据中心,跨机房服务永远查不到——不是网络不通,是请求根本没发过去。Consul 的 HTTP API 默认把 Datacenter 当作 query 参数,但 Go 客户端不自动透传,必须手动塞进 api.QueryOptions 里。
常见错误现象:服务在 remote DC 注册成功、UI 可见,但本地调用 client.Health().Service("svc", "", true, nil) 返回空;curl "http://consul:8500/v1/health/service/svc?dc=us-west" 却能拿到结果。
- 正确做法:构造
opts := &api.QueryOptions{Datacenter: "us-west"},传给第三个参数(不是第四个) - 注意:
client.Health().Service("svc", "", true, opts)中第四个参数是ctx,别把opts放错位置 - 如果目标 DC 名含下划线或大小写混用(如
prod-eu),确保 Consul server 配置里datacenter = "prod-eu"严格一致,大小写敏感
服务注册时 Address 必须填本机可被 remote DC 访问的 IP
你在 api.AgentServiceRegistration 里填的 Address,是 remote DC 的 Consul agent 去做健康检查时 curl 的目标地址。填 "localhost" 或容器内网 IP(如 172.18.0.5),remote DC 根本连不上,健康检查永远 critical,服务自动被剔除。
使用场景:K8s 集群跨 region 部署,或物理机房之间通过专线互联。
- 多网卡机器上,用
net.InterfaceAddrs()扫描非 loopback IPv4 地址,优先选公网或专线网段 IP - Docker 环境避免用
host.docker.internal——它只对单机有效,跨机房无效;应走 service mesh 或 ingress 显式暴露 - HTTP 健康检查 URL 必须完整,比如
"http://203.0.113.42:8080/health",不能省略协议和端口
Watch services 跨 DC 必须带 Datacenter 且手动重连
api.WatchPlan 的 Handler 不会自动带上 Datacenter,也不处理连接断开。你设了 Key: "services/",它默认只监听本地 DC,remote DC 服务上线完全收不到通知。
容易踩的坑:以为 Watch 是“全局监听”,实际它是单 DC 长连接,超时或网络抖动后静默退出,后续变更彻底丢失。
- Watch 构造时必须显式加
QueryOptions: &api.QueryOptions{Datacenter: "us-west"} -
Run()返回 error 后,必须自己重启 Watch,官方 client 不提供自动重试 - 不要复用同一个
WatchPlan实例监听多个 DC;每个 DC 单独起 goroutine + 独立Run()
ACL Token 在 multi-DC 场景下需全局生效
Consul ACL token 不是 per-DC 的,而是 cluster-wide。但如果你在某个 DC 的 server 上创建 token,没同步到其他 DC 的 server,就会出现:本地 DC 查询正常,remote DC 返回空列表且无报错——权限被静默拒绝。
真实项目里,token 权限粒度要细:至少给 service:read 和 node:read,否则 Health().Service() 查不到节点元数据。
- 用
consul acl token create -policy-name="service-read"创建,确保 policy 绑定到所有 DC 的 server - Go 客户端 config 中
Token字段必须设,空字符串 ≠ 无认证,等于触发 deny-all 默认策略 - 调试时临时禁用 ACL:启动 Consul 时加
-acl-disabled,确认逻辑通后再开 ACL
consul operator raft get-peers 每个 DC 单独跑一遍,看 peer list 是否完整,Raft leader 是否稳定。一旦 WAN gossip 断开,服务发现就退化成单 DC 局部视图。

















