2. 服务注册与发现工程实践

服务注册与发现是微服务架构的“通讯录”。服务实例启动后把自己的地址写入注册中心,消费者通过注册中心查询可用实例列表,再借助客户端负载均衡发起调用。注册中心一旦不可用,整个调用链会迅速退化,因此工程实践的重点不只是“能注册”,而是“选型合理、部署高可用、健康检查准确、异常时行为可控”。本节围绕 Eureka、Consul、Nacos 的差异,注册中心高可用部署,以及健康检查与自我保护机制展开。

一、三种注册中心的核心差异

Eureka 是 Netflix 开源的 AP 型注册中心,强调可用性:节点之间通过复制同步注册信息,客户端本地缓存服务列表,即使注册中心短时不可用,调用仍可依靠缓存继续。它采用客户端心跳(默认 30 秒)判断实例存活,并带有自我保护机制。Consul 基于 Raft 协议,是 CP 型,提供强一致性的 KV、服务发现和健康检查,支持 DNS 与 HTTP 两种查询方式,健康检查类型丰富(脚本、HTTP、TCP、TTL)。Nacos 同时支持 AP 与 CP 两种一致性模式,集服务发现与配置管理于一体,支持临时实例(心跳)与持久实例(服务端主动探测),并提供控制台、权重、元数据、命名空间与分组等能力,在国内生态中使用广泛。

维度EurekaConsulNacos
一致性模型APCP(Raft)AP/CP 可选
健康检查客户端心跳服务端多种探测心跳 + 服务端探测
配置中心无有 KV内置配置管理
控制台简单有功能完善
典型场景Spring Cloud Netflix 体系多语言、强一致需求Spring Cloud Alibaba 体系

选型建议:已有 Spring Cloud Netflix 存量系统可继续用 Eureka;需要强一致、多语言、DNS 发现可选 Consul;新建项目、需要配置中心一体化、希望有完善控制台,优先 Nacos。

二、注册中心高可用部署

单节点注册中心是单点故障,生产必须集群化。Eureka 采用 peer-to-peer 复制,节点之间互相注册;Consul 用 Raft,通常 3 或 5 个 Server 节点;Nacos 集群依赖 MySQL 存储,多个 Nacos 节点共享同一数据库,前面挂负载均衡。

以 Nacos 三节点集群为例,关键配置如下:

# application.properties(每个节点)
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://192.168.1.10:3306/nacos_config?useUnicode=true&characterEncoding=utf8&autoReconnect=true
db.user=nacos
db.password=nacos

# cluster.conf(三节点)
192.168.1.11:8848
192.168.1.12:8848
192.168.1.13:8848

客户端配置多个地址,避免单点:

spring.cloud.nacos.discovery.server-addr=192.168.1.11:8848,192.168.1.12:8848,192.168.1.13:8848

Eureka 集群则通过 eureka.client.service-url.defaultZone 互相指向其他节点,例如节点 A 指向 B、C,节点 B 指向 A、C,形成对等复制。Consul 集群使用 consul join 将 Server 节点加入同一 Raft 组,并保证奇数节点数以获得多数派容错。

三、健康检查与自我保护机制

健康检查决定“哪些实例可以被调用”。Eureka 默认客户端每 30 秒发送心跳,服务端 90 秒未收到心跳则剔除实例。当心跳丢失比例超过阈值(默认 85%),Eureka 进入自我保护模式,停止剔除实例,避免网络抖动导致大量误删。自我保护是双刃剑:网络分区时保护了实例列表,但也可能让消费者调用到已宕机实例,因此需要配合客户端重试与熔断。

Nacos 临时实例使用心跳,默认 5 秒一次,15 秒未收到标记不健康,30 秒未收到则剔除;持久实例由服务端主动探测。Consul 健康检查可配置 HTTP 探测:

{
  "service": {
    "name": "order-service",
    "port": 8081,
    "check": {
      "http": "http://192.168.1.21:8081/actuator/health",
      "interval": "10s",
      "timeout": "3s",
      "deregister_critical_service_after": "30s"
    }
  }
}

Spring Boot 应用应暴露 /actuator/health,并让注册中心的健康检查指向该端点,而不是仅依赖端口存活。这样数据库连接失败、依赖不可用等业务级异常也能反映到实例状态。

健康检查间隔、超时、剔除阈值需要与业务容忍度匹配:间隔过短会增加注册中心压力,过长则故障发现慢。建议心跳间隔 5~10 秒,剔除时间 30~60 秒,并结合客户端重试。

四、工程实践中的关键判断

部署完成后,需要验证三件事:一是注册中心集群任意节点宕机后,服务仍能注册与发现;二是实例被强制下线后,消费者能在预期时间内不再调用它;三是网络抖动时,自我保护或健康检查行为符合预期。可以通过控制台观察实例列表、健康状态与心跳时间,也可以直接调用注册中心 API 查询。

下面这张图概括了服务注册、健康检查、实例剔除与消费者拉取列表之间的信号关系,帮助理解注册中心在调用链中的位置。

服务注册与发现信号关系图 服务实例 A order-service:8081 /actuator/health 服务实例 B order-service:8082 /actuator/health 注册中心集群 Eureka / Consul / Nacos 节点 1(Leader) 节点 2(Follower) 节点 3(Follower) 服务消费者 user-service 本地缓存实例列表 注册 注册 心跳 5~30s 心跳 5~30s 拉取列表 负载均衡调用实例 健康检查失败 / 心跳超时 标记不健康 → 剔除实例 超时/失败 自我保护模式 心跳丢失 > 85% 时暂停剔除 注册/拉取 心跳 剔除 调用 说明:实线为注册/查询,虚线为心跳与调用;红色表示异常剔除路径,黄色表示自我保护路径。

从图中可以看到,注册中心位于服务实例与消费者之间,承担注册、心跳、健康检查、列表拉取与剔除职责。高可用部署保证注册中心自身不成为单点,健康检查保证列表中的实例真实可用,自我保护则在网络异常时避免误删。三者配合,才能让服务发现既稳定又准确。

自我保护开启时,消费者可能拿到已宕机实例。务必在客户端配置重试(如 Spring Cloud LoadBalancer 重试)与熔断降级,不能完全依赖注册中心的剔除速度。

最后给出一个可执行的验证清单:启动三节点注册中心,停掉其中一个节点,观察服务是否仍能注册与发现;启动两个服务实例,停掉一个,观察消费者是否在剔除时间内停止调用;模拟网络延迟,观察心跳与健康检查状态变化。通过控制台和日志确认行为符合预期,才算完成注册与发现的工程落地。