本页目录

服务发现

在动态扩缩的集群里,调用方怎么知道该连哪个 IP?服务发现把"注册→健康检查→查询"做成标准流程:consul/etcd 存注册表,DNS-SD 和客户端负载均衡是两种不同的查询路径,health check 决定"这个实例还能不能接流量"。

问题

服务实例的 IP:port 是动态变化的(扩缩容、滚动更新、故障迁移)。调用方如何知道"现在有哪些实例可用"?

DNS-SD (Service Discovery via DNS)

利用成熟的 DNS 基础设施做服务发现:

服务: api-service → DNS SRV records:
  _api._tcp.example.com SRV 10 60 8080 instance-1.example.com
  _api._tcp.example.com SRV 10 40 8080 instance-2.example.com

client: dig SRV _api._tcp.example.com → [instance-1:8080, instance-2:8080]

优势:无需额外组件。局限:DNS TTL 决定变更延迟(通常 30-60s),没有原生健康检查。

Consul/etcd 服务注册

服务实例启动 → register to Consul/etcd (with health check URL) → client 查询 → 返回 healthy instances:

Consul/etcd 服务注册与发现:注册 → 健康检查 → 客户端查询 Instance-1 Instance-2 Consul Server Consul Agent 健康检查 GET /health Client register(host:8080) register(host:8080) GET /v1/health/service/api (返回 healthy 实例列表) register 只建立注册关系;真正决定「能不能被发现」的是 Consul Agent 持续的健康检查。

支持:HTTP API、DNS 接口(兼容 DNS-SD)、long-poll watch(变更推)。

Client-side vs Server-side LB

Client-side:
  [Client] → 查询服务发现 → 得到 [I1, I2, I3] → 自己选一个 → 直连
  例子: gRPC (with Consul/etcd resolver), Finagle, netflix-eureka

Server-side:
  [Client] → LB (固定地址) → LB → [I1, I2, I3]
  例子: kube-proxy, nginx upstream, HAProxy

对比:client-side 少一跳(latency↓),但 client 要维护服务发现逻辑。Server-side client 无需感知后端变化,但 LB 成为瓶颈和单点。

Health Check

  • Passive: 从实际请求的 failure(HTTP 5xx, TCP RST)判断。不需要额外请求,但可能被偶尔的 transient error 误判
  • Active: 定期 GET /health → 期望 200。可靠但增加负载
  • TTL-based (Consul): agent 定期向 Consul 报告健康状态。超时未收到 report → 标记 unhealthy → 从发现中移除

参考

  • Consul: consul.io/docs/discovery
  • etcd: etcd.io/docs
  • gRPC name resolution: github.com/grpc/grpc/blob/master/doc/naming.md

Keywords: service discovery, DNS-SD, Consul, etcd, health check, client-side LB, server-side LB