【架构实战】注册中心选型:Nacos vs Eureka vs Consul

一、Eureka宕机让我们全站瘫痪

2019年双十一后的凌晨3点,我接到告警电话:订单服务大面积超时。

登上服务器一看,Eureka Server的进程不见了。查看日志,发现是内存泄漏导致OOM崩溃。

虽然Eureka有客户端缓存机制,但问题在于:

  • 我们的Eureka Client配置的registryFetchIntervalSeconds=30s
  • 缓存有效期只有30秒,过期后客户端需要重新拉取
  • Eureka Server挂了,拉取失败,本地缓存变空
  • 所有服务发现调用返回空列表,RPC调用开始大面积失败

更悲剧的是,我们的熔断器配置了"当实例数为0时直接熔断",导致整个服务链路瞬间熔断,用户下单全部失败。

故障复盘时我们意识到:

Eureka的AP模型虽然保证了可用性,但牺牲了一致性。当Eureka Server宕机后,客户端缓存的过期策略反而成了故障扩散的原因。

从那以后,我们开始重新评估注册中心的选型,最终迁移到了Nacos。


二、注册中心对比

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                  注册中心对比                                      │
│                                                                  │
│  特性        │ Eureka     │ Nacos      │ Consul     │ ZooKeeper │
│  ─────────────────────────────────────────────────────────────  │
│  CAP        │ AP         │ AP/CP      │ CP         │ CP        │
│  健康检查    │ 客户端心跳 │ 心跳+TCP   │ Agent+HTTP │ 会话      │
│  配置中心    │ 否         │ 是         │ 是(KV)     │ 否        │
│  雪崩保护    │ 是         │ 是         │ 否         │ 否        │
│  自动注销    │ 否(等过期) │ 是         │ 是         │ 是        │
│  一致性协议  │ 无         │ Distro/Raft│ Raft       │ ZAB       │
│  K8s集成    │ 否         │ 是         │ 是         │ 否        │
│  管理界面    │ 简陋       │ 完善       │ 完善       │ 无        │
│  社区活跃    │ 停更       │ 活跃       │ 活跃       │ 活跃      │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

2.1 Eureka的优缺点

优点:

  • Spring Cloud原生支持,开箱即用
  • AP模型,保证可用性
  • 简单易用,学习成本低

缺点:

  • 已停止维护(2.x版本官方宣布不再开源)
  • 只支持AP,不适合强一致性场景
  • 管理界面简陋,排查问题困难
  • 健康检查依赖客户端心跳,不够精准

2.2 Nacos的优缺点

优点:

  • 同时支持AP和CP模式,可切换
  • 集成配置中心,一石二鸟
  • 管理界面完善,服务治理方便
  • 阿里开源,社区活跃,国内生态好
  • 支持DNS协议,K8s集成友好

缺点:

  • 功能多意味着复杂度高
  • 配置项繁多,需要理解透彻
  • 集群部署相对复杂

2.3 Consul的优缺点

优点:

  • CP模型,强一致性
  • 自带服务网格(Connect)
  • 支持多数据中心
  • K8s集成原生支持

缺点:

  • 需要部署Agent,运维成本高
  • 国内社区相对小众
  • 配置中心能力较弱(只有KV)

2.4 ZooKeeper的优缺点

优点:

  • CP模型,强一致性
  • 成熟稳定,久经考验
  • Hadoop/Kafka等大数据生态默认选择

缺点:

  • 不是专门的服务发现组件,使用复杂
  • 无管理界面,调试困难
  • 性能较差,不适合大规模服务

三、Nacos实战

3.1 服务注册

java 复制代码
/**
 * Nacos服务注册配置
 */
@Configuration
public class NacosRegisterConfig {
    
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

# bootstrap.yml
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: nacos:8848
        namespace: production
        group: DEFAULT_GROUP
        cluster-name: HZ
        weight: 1
        metadata:
          version: v2
          region: hangzhou

3.2 服务发现

java 复制代码
/**
 * 服务发现使用
 */
@Service
@Slf4j
public class ServiceDiscoveryService {
    
    @Autowired
    private DiscoveryClient discoveryClient;
    
    @Autowired
    private NacosServiceManager nacosServiceManager;
    
    /**
     * 获取服务实例
     */
    public List<ServiceInstance> getInstances(String serviceId) {
        return discoveryClient.getInstances(serviceId);
    }
    
    /**
     * 获取所有服务
     */
    public List<String> getServices() {
        return discoveryClient.getServices();
    }
    
    /**
     * 订阅服务变更
     */
    public void subscribe(String serviceId, EventListener listener) {
        nacosServiceManager.getNamingService().subscribe(
            serviceId, 
            event -> {
                if (event instanceof NamingEvent) {
                    NamingEvent namingEvent = (NamingEvent) event;
                    log.info("服务变更: service={}, instances={}", 
                        serviceId, namingEvent.getInstances().size());
                }
            }
        );
    }
}

3.3 Nacos配置中心

java 复制代码
/**
 * Nacos配置中心使用
 */
@Service
@Slf4j
public class NacosConfigService {
    
    /**
     * 动态配置
     */
    @NacosValue(value = "${order.max-retry:3}", autoRefreshed = true)
    private int maxRetry;
    
    /**
     * 配置变更监听
     */
    @NacosConfigListener(dataId = "order-service.properties", groupId = "DEFAULT_GROUP")
    public void onConfigChange(String newConfig) {
        log.info("配置变更: {}", newConfig);
        // 刷新本地配置
    }
}

3.4 集群部署配置

bash 复制代码
# Nacos集群配置 cluster.conf
192.168.1.1:8848
192.168.1.2:8848
192.168.1.3:8848

# 使用MySQL存储(生产环境必须)
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://mysql:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=nacos
db.password.0=nacos

四、踩坑实录

坑1:临时实例vs持久实例

问题:Nacos默认使用临时实例(ephemeral=true),靠心跳保活。服务假死时心跳线程可能还在运行,注册中心不会剔除,流量打到假死实例上。

踩坑场景

我们有个服务因为死锁卡住了,但心跳线程还在,Nacos认为服务健康,继续把流量打过去,导致大量请求超时。

解决方案

yaml 复制代码
spring:
  cloud:
    nacos:
      discovery:
        # 配置健康检查
        heart-beat-interval: 5000  # 心跳间隔5秒
        heart-beat-timeout: 15000  # 心跳超时15秒
        # 或者使用持久实例
        ephemeral: false

坑2:注册信息不完整

问题:只注册了IP和端口,没注册权重、版本等元数据,导致灰度路由失败。

踩坑场景

我们要做灰度发布,按版本号路由流量,结果发现服务实例没有注册version元数据,灰度规则全部失效。

解决方案

yaml 复制代码
spring:
  cloud:
    nacos:
      discovery:
        metadata:
          version: v2.0.0
          region: hangzhou
          env: gray
          weight: 80

坑3:注册中心集群脑裂

问题:Nacos集群网络分区,出现两个Leader,服务注册信息不一致。

踩坑场景

机房网络抖动,Nacos集群出现脑裂,不同客户端看到的服务列表不一致,导致请求时好时坏。

解决方案

properties 复制代码
# 切换到CP模式(强一致性)
nacos.core.protocol.raft.data.enabled=true

# 增加选举超时时间
nacos.core.protocol.raft.data.election-timeout-ms=5000

坑4:服务下线不优雅

问题:服务直接kill,注册中心等心跳超时才剔除,期间请求还在打到已下线的实例。

踩坑场景

发布时直接kill进程,客户端还在调用下线实例,报大量连接拒绝错误。

解决方案

java 复制代码
@Bean
public GracefulShutdown gracefulShutdown() {
    return new GracefulShutdown();
}

// 或使用Spring Boot的优雅关闭
server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

坑5:Nacos写入压力

问题:1000个服务同时上线,Nacos写入压力大,出现写入延迟。

踩坑场景

凌晨批量发布,几百个服务同时启动注册,Nacos扛不住,注册延迟长达30秒,导致服务启动后找不到依赖。

解决方案

  • 分批上线,错峰启动
  • 增加Nacos集群节点
  • 调整写入批处理参数

坑6:命名空间隔离不彻底

问题:以为用了namespace就能完全隔离,结果配置还是串了。

踩坑场景

我们用namespace区分dev/test/prod环境,但有个服务的配置没指定namespace,默认用了public,结果覆盖了其他环境的配置。

解决方案

yaml 复制代码
spring:
  cloud:
    nacos:
      discovery:
        namespace: ${NACOS_NAMESPACE:public}
      config:
        namespace: ${NACOS_NAMESPACE:public}

五、选型建议

5.1 按场景选型

场景 推荐方案 理由
Spring Cloud生态 Nacos 无缝集成,功能全面
Kubernetes原生 Consul 原生支持,多数据中心
简单Demo项目 Eureka 开箱即用(但不推荐生产)
强一致性要求 ZooKeeper CP模型,金融场景
国内团队 Nacos 中文文档,社区活跃

5.2 最佳实践

  1. 优先选择Nacos(功能完善、社区活跃、国内生态好)
  2. 注册时携带完整元数据(版本、区域、权重、环境等)
  3. 配置合理的健康检查(心跳间隔、超时时间、健康检查URL)
  4. 优雅上下线(主动注销、优雅关闭、预热)
  5. 做好注册中心监控(服务数量、实例健康率、注册延迟)
  6. 多环境隔离(使用namespace隔离dev/test/prod)
  7. 集群部署(至少3节点,使用MySQL存储)

六、血的教训

注册中心是微服务的基础设施,选型要慎重。

我们当初从Eureka迁移到Nacos,花了整整两周:

  • 改造所有服务的注册配置
  • 迁移配置中心
  • 重新设计灰度发布流程
  • 全链路测试

迁移成本极高,选型时一定要考虑长远。

Eureka已经停止维护了,新项目千万别用。


七、思考题

  1. 你的系统用了什么注册中心?选型时考虑了哪些因素?
  2. 遇到过注册中心故障吗?怎么解决的?
  3. 你认为注册中心最重要的是什么?可用性还是一致性?

个人观点,仅供参考

相关推荐
java_cj1 小时前
阅读 k8s 源码的准备工作
云原生·容器·kubernetes
国产化创客1 小时前
嵌入式视觉完整技术体系--ESP32/K230/RDK-X5/树莓派四层架构全解析
嵌入式硬件·物联网·架构·开源·智能硬件
AI科技星2 小时前
数术工坊:投影秘籍
人工智能·线性代数·架构·概率论·学习方法
JAMSAN09302 小时前
16.0% 高增长!全球异构计算架构服务市场扩容态势
汇编·人工智能·架构
Godspeed Zhao2 小时前
跨越天际:从智能汽车到 eVTOL 的适航与系统级开发21——时间触发以太网(TTE)与 ARINC 664(AFDX)
架构·汽车·php
段一凡-华北理工大学2 小时前
工业领域的Hadoop架构学习~系列文章23:物流行业Hadoop应用实践 - 智能物流的数字化引擎
大数据·人工智能·hadoop·分布式·学习·架构·高炉炼铁
峥无2 小时前
MySQL 系统学习之路 第一篇:服务安装、基础概念与架构全解
学习·mysql·架构
Jabes.yang2 小时前
互联网大厂Java求职面试实战解析(含技术场景与详解)
spring boot·微服务·面试·orm·技术栈·java se·jakarta ee