如此轻松的拿捏Nacos精华

摘要:从一次"微服务找不到彼此"的调用失败出发,深度剖析Nacos服务注册与发现、配置中心、健康检查的核心原理。通过AP/CP模式的权衡选择、临时实例与持久化实例的区别、以及配置热更新的推送机制,揭秘为什么Nacos能替代Eureka+Config+Bus三个组件、双注册中心如何保证高可用、以及Nacos集群的部署架构。配合时序图展示服务调用流程,给出生产环境的最佳实践。


💥 翻车现场

周一早上,哈吉米部署了第一个微服务应用。

架构:
订单服务 → 调用 → 库存服务

代码

// 订单服务
@FeignClient(name = "stock-service", url = "http://192.168.1.20:8081")
public interface StockClient {
    @GetMapping("/stock/decrease")
    Result decrease(@RequestParam Long productId);
}

@Service
public class OrderService {
    
    @Autowired
    private StockClient stockClient;
    
    public void createOrder(Order order) {
        // 调用库存服务
        stockClient.decrease(order.getProductId());
    }
}

测试:运行正常 ✅

上线第一天:库存服务扩容到3台

库存服务:
192.168.1.20:8081
192.168.1.21:8081  ← 新增
192.168.1.22:8081  ← 新增

问题

订单服务还是调用:http://192.168.1.20:8081
→ 其他2台机器空闲
→ 192.168.1.20压力大,经常超时

哈吉米:"卧槽,url写死了,无法负载均衡!而且如果192.168.1.20挂了,整个订单服务都不可用!"

技术总监:"用Nacos做服务注册与发现,动态感知服务实例。"
哈吉米:"Nacos是啥?"

南北绿豆和阿西噶阿西来了。

南北绿豆:"Nacos = Naming + Configuration Service,服务注册发现 + 配置中心。"
阿西噶阿西:"来,我给你讲讲Nacos的核心功能。"


🤔 Nacos是什么?解决什么问题?

传统方式的问题

问题1:服务地址写死

@FeignClient(url = "http://192.168.1.20:8081")  // 写死

问题

  • ❌ 服务扩容,无法自动发现新实例
  • ❌ 服务下线,仍然调用(失败)
  • ❌ 无法负载均衡

问题2:配置文件分散

每个服务都有自己的application.yml:
- 订单服务:application.yml(数据库配置、Redis配置...)
- 库存服务:application.yml
- 支付服务:application.yml

问题:
- ❌ 配置重复(每个服务都要配Redis地址)
- ❌ 修改配置要重启(改Redis地址 → 重启所有服务)
- ❌ 配置不统一(订单服务用Redis-1,库存服务用Redis-2)

Nacos的解决方案

功能1:服务注册与发现

服务启动时:
库存服务启动 → 注册到Nacos(IP:192.168.1.20:8081)
库存服务扩容 → 新实例注册到Nacos(IP:192.168.1.21:8081)

订单服务调用:
订单服务 → 从Nacos获取库存服务的实例列表
→ [192.168.1.20:8081, 192.168.1.21:8081, 192.168.1.22:8081]
→ 负载均衡选择一个调用 ✅

功能2:配置中心

配置统一存储在Nacos:
- 数据库配置(MySQL地址、用户名、密码)
- Redis配置
- MQ配置
- 业务参数(限流阈值、开关)

修改配置:
Nacos控制台修改 → 实时推送给所有服务 → 服务自动刷新 ✅

阿西噶阿西:"Nacos = Eureka(服务发现)+ Config(配置中心)+ Bus(配置推送),一个组件顶三个!"


🎯 核心功能1:服务注册与发现

服务注册流程

// 1. 引入Nacos依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

// 2. 配置Nacos地址
spring:
  application:
    name: stock-service  # 服务名
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.100:8848  # Nacos地址

// 3. 启动类加注解
@SpringBootApplication
@EnableDiscoveryClient  // 开启服务发现
public class StockServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(StockServiceApplication.class, args);
    }
}

// 4. 服务启动时,自动注册到Nacos

注册流程图

sequenceDiagram
    participant StockSvc as 库存服务
    participant Nacos as Nacos Server
    participant OrderSvc as 订单服务

    StockSvc->>StockSvc: 1. 应用启动
    StockSvc->>Nacos: 2. 注册实例<br/>name=stock-service<br/>ip=192.168.1.20, port=8081
    Nacos->>Nacos: 3. 存储实例信息
    Nacos->>StockSvc: 4. 注册成功
    
    loop 心跳保活(每5秒)
        StockSvc->>Nacos: 5. 发送心跳
        Nacos->>StockSvc: 6. 心跳响应
    end
    
    OrderSvc->>Nacos: 7. 查询stock-service的实例列表
    Nacos->>OrderSvc: 8. 返回:[192.168.1.20:8081, 192.168.1.21:8081]
    
    OrderSvc->>OrderSvc: 9. 负载均衡选择一个实例
    OrderSvc->>StockSvc: 10. 调用192.168.1.20:8081

服务发现代码

// 订单服务
@FeignClient(name = "stock-service")  // 不用写url,从Nacos获取
public interface StockClient {
    
    @GetMapping("/stock/decrease")
    Result decrease(@RequestParam Long productId);
}

@Service
public class OrderService {
    
    @Autowired
    private StockClient stockClient;
    
    public void createOrder(Order order) {
        // Feign自动从Nacos获取stock-service的实例
        // 自动负载均衡(默认轮询)
        stockClient.decrease(order.getProductId());
    }
}

负载均衡策略

# 配置负载均衡策略
stock-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule  # 随机

# 或者
stock-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule  # 轮询(默认)

策略对比

策略说明适用场景
轮询(默认)依次调用每个实例实例性能相同
随机随机选择实例通用
权重按权重分配流量实例性能不同
最少连接选择连接数最少的实例长连接场景

🎯 核心功能2:配置中心

配置中心的价值

问题场景

修改Redis地址(192.168.1.10 → 192.168.1.20)

传统方式:
1. 修改订单服务的application.yml
2. 修改库存服务的application.yml
3. 修改支付服务的application.yml
4. 重启订单服务
5. 重启库存服务
6. 重启支付服务

问题:
- ❌ 每个服务都要改
- ❌ 必须重启(有停机时间)

Nacos方式

1. Nacos控制台修改Redis配置
2. Nacos推送配置给所有服务
3. 服务自动刷新配置(不重启)✅

耗时:10秒
停机时间:0

配置中心代码

// 1. 引入依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

// 2. 配置Nacos地址(bootstrap.yml)
spring:
  application:
    name: order-service
  cloud:
    nacos:
      config:
        server-addr: 192.168.1.100:8848
        file-extension: yaml
        namespace: dev  # 环境隔离

// 3. Nacos控制台创建配置
Data ID: order-service.yaml
Group: DEFAULT_GROUP
配置内容:
spring:
  redis:
    host: 192.168.1.10
    port: 6379

// 4. 代码中使用
@Configuration
@RefreshScope  // 关键:支持配置热更新
public class RedisConfig {
    
    @Value("${spring.redis.host}")
    private String redisHost;
    
    @Value("${spring.redis.port}")
    private int redisPort;
    
    @Bean
    public RedisTemplate redisTemplate() {
        // 使用配置
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(
            new LettuceConnectionFactory(redisHost, redisPort)
        );
        return template;
    }
}

配置热更新流程

sequenceDiagram
    participant Admin as 运维
    participant Nacos as Nacos Server
    participant OrderSvc as 订单服务
    participant StockSvc as 库存服务

    Admin->>Nacos: 1. 修改Redis配置<br/>host: 192.168.1.10 → 192.168.1.20
    Nacos->>Nacos: 2. 保存配置,版本+1
    
    par 推送配置
        Nacos->>OrderSvc: 3. 推送新配置(长轮询)
        Nacos->>StockSvc: 4. 推送新配置
    end
    
    OrderSvc->>OrderSvc: 5. 刷新@RefreshScope的Bean
    Note over OrderSvc: redisHost: 192.168.1.10 → 192.168.1.20<br/>不重启 ✅
    
    StockSvc->>StockSvc: 6. 刷新@RefreshScope的Bean
    Note over StockSvc: 配置已更新
    
    Note over OrderSvc,StockSvc: 全部服务配置已更新<br/>无需重启

南北绿豆:"看到了吗?配置修改后,Nacos自动推送给所有服务,服务热更新,不用重启!"


🎯 核心功能3:健康检查

健康检查机制

Nacos的健康检查有2种

模式1:客户端主动上报(临时实例)

服务启动 → 注册到Nacos → 每5秒发送心跳

Nacos判断:
- 15秒内收到心跳 → 健康 ✅
- 15秒未收到心跳 → 不健康 ❌(从服务列表移除)

特点:
- 客户端主动上报
- 服务挂了,停止心跳,Nacos自动摘除
- 适合:临时服务(容器、微服务)

模式2:服务端主动探测(持久化实例)

Nacos主动探测:
每20秒向服务发送HTTP/TCP请求

判断:
- 响应正常 → 健康 ✅
- 响应超时/失败 → 不健康 ❌(标记为下线,但不删除)

特点:
- 服务端主动探测
- 即使服务挂了,实例信息仍保留
- 适合:持久化服务(传统部署)

临时实例 vs 持久化实例

配置

spring:
  cloud:
    nacos:
      discovery:
        ephemeral: true  # 临时实例(默认)
        # ephemeral: false  # 持久化实例

对比

特性临时实例(AP)持久化实例(CP)
健康检查客户端主动上报心跳服务端主动探测
服务挂了从列表移除标记不健康,保留实例
适用场景微服务、容器传统部署、虚拟机
一致性模型AP(可用性优先)CP(一致性优先)

南北绿豆:"微服务推荐用临时实例,容器随时可能销毁重建,临时实例更合适。"


🎯 AP模式 vs CP模式

什么是AP和CP?

CAP理论

C(Consistency):一致性
A(Availability):可用性
P(Partition Tolerance):分区容错性

定理:最多只能同时满足2个

分布式系统必须P,所以只能在C和A之间选择:
- CP:牺牲可用性,保证一致性(Zookeeper、Consul)
- AP:牺牲一致性,保证可用性(Eureka、Nacos临时实例)

Nacos的CP模式

场景:持久化实例

Nacos集群(3个节点):
节点1、节点2、节点3

注册服务:
1. 客户端向节点1注册
2. 节点1写入数据,同步给节点2、节点3
3. 超过半数确认(2个) → 注册成功
4. 返回客户端

网络分区:
节点1、节点2在分区A(2个节点)
节点3在分区B(1个节点)

结果:
- 分区A有2个节点(过半数),可以处理注册请求 ✅
- 分区B只有1个节点(未过半数),拒绝注册请求 ❌

特点:
- ✅ 强一致性(数据不会丢)
- ❌ 分区B不可用(牺牲可用性)

Nacos的AP模式

场景:临时实例

Nacos集群(3个节点):
节点1、节点2、节点3

注册服务:
1. 客户端向节点1注册
2. 节点1立即返回成功(不等待同步)
3. 后台异步同步给节点2、节点3

网络分区:
节点1、节点2在分区A
节点3在分区B

结果:
- 分区A可以处理注册 ✅
- 分区B也可以处理注册 ✅(但数据可能不一致)

特点:
- ✅ 高可用(所有分区都可用)
- ❌ 弱一致性(可能短暂不一致,最终一致)

如何选择?

场景推荐模式原因
微服务注册AP可用性更重要,短暂不一致可接受
配置中心CP一致性更重要,配置错误会出大问题
分布式锁CP必须强一致
服务发现AP服务列表短暂不一致可接受

阿西噶阿西:"Nacos的设计很巧妙:临时实例用AP,持久化实例用CP,根据场景自动选择。"


🎯 Nacos的注册表结构

服务实例的存储

Nacos的注册表(内存Map):

Map<String, Service> serviceMap = {
  "stock-service": {
    name: "stock-service",
    instances: [
      {
        ip: "192.168.1.20",
        port: 8081,
        healthy: true,
        weight: 1.0,
        metadata: {...}
      },
      {
        ip: "192.168.1.21",
        port: 8081,
        healthy: true,
        weight: 1.0
      }
    ]
  },
  "order-service": {
    ...
  }
}

实例字段

字段含义示例
ip服务IP192.168.1.20
port服务端口8081
healthy是否健康true/false
weight权重(负载均衡用)1.0
metadata元数据(自定义信息){version: "1.0"}

🎯 配置中心的3个核心特性

特性1:配置分层

Nacos配置的3个维度:
1. Namespace(命名空间):环境隔离
   - dev(开发环境)
   - test(测试环境)
   - prod(生产环境)

2. Group(分组):项目隔离
   - DEFAULT_GROUP(默认)
   - ORDER_GROUP(订单项目)
   - STOCK_GROUP(库存项目)

3. Data ID(配置文件):具体配置
   - order-service.yaml
   - common-redis.yaml
   - common-mysql.yaml

层级关系

Namespace: prod
  ├─ Group: DEFAULT_GROUP
  │   ├─ order-service.yaml
  │   └─ stock-service.yaml
  └─ Group: COMMON_GROUP
      ├─ redis.yaml
      └─ mysql.yaml

特性2:配置热更新

// 方法1:@RefreshScope(推荐)
@Configuration
@RefreshScope  // 配置变化时,自动刷新这个Bean
public class RedisConfig {
    
    @Value("${spring.redis.host}")
    private String redisHost;
    
    @Bean
    public RedisTemplate redisTemplate() {
        // 配置变化时,这个Bean会被重建
        return new RedisTemplate();
    }
}

// 方法2:@NacosValue(监听单个配置)
@Component
public class LimitConfig {
    
    @NacosValue(value = "${rate.limit.qps}", autoRefreshed = true)
    private int qps;  // 配置变化时,自动更新
    
    public int getQps() {
        return qps;
    }
}

// 方法3:监听器(自定义处理)
@Component
public class ConfigListener {
    
    @NacosConfigListener(dataId = "order-service.yaml")
    public void onConfigChange(String newConfig) {
        System.out.println("配置变化:" + newConfig);
        // 自定义处理逻辑
    }
}

特性3:配置版本管理

Nacos配置历史:

版本1(2024-10-01 10:00):
spring.redis.host=192.168.1.10

版本2(2024-10-05 14:30):
spring.redis.host=192.168.1.20

版本3(2024-10-07 09:15):
spring.redis.host=192.168.1.30

功能:
- 查看历史版本
- 对比版本差异
- 回滚到历史版本

控制台操作

配置管理 → 历史版本 → 选择版本2 → 回滚

→ 所有服务自动回滚到版本2的配置

🎯 Nacos集群架构

集群部署

Nacos集群(3个节点,推荐奇数):

节点1:192.168.1.101:8848
节点2:192.168.1.102:8848
节点3:192.168.1.103:8848

客户端配置:
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.101:8848,192.168.1.102:8848,192.168.1.103:8848

特点:
- 节点之间互相同步数据
- 客户端随机连接一个节点
- 某个节点挂了,客户端自动切换到其他节点

数据同步机制

临时实例(AP模式)

Distro协议(Nacos自研):

节点分片:
- 节点1负责:stock-service
- 节点2负责:order-service
- 节点3负责:payment-service

注册流程:
1. stock-service注册到节点1(随机选择)
2. 节点1存储实例信息
3. 节点1异步同步给节点2、节点3
4. 最终一致

持久化实例(CP模式)

Raft协议:

Leader节点:节点1
Follower节点:节点2、节点3

注册流程:
1. 客户端注册到节点1(Leader)
2. 节点1写入日志,复制给节点2、节点3
3. 收到多数确认(2个) → 提交
4. 返回客户端

🎓 面试标准答案

题目:Nacos有哪些核心功能?

答案

3大核心功能

1. 服务注册与发现

  • 服务启动时注册到Nacos
  • 其他服务从Nacos获取实例列表
  • 自动负载均衡
  • 健康检查(心跳或探测)

2. 配置中心

  • 统一管理配置
  • 配置热更新(不重启)
  • 版本管理(回滚)
  • 环境隔离(Namespace)

3. 健康检查

  • 临时实例:客户端心跳
  • 持久化实例:服务端探测
  • 不健康实例自动摘除

AP vs CP

  • 临时实例:AP模式(Distro协议)
  • 持久化实例:CP模式(Raft协议)

优势

  • 一个组件顶三个(Eureka + Config + Bus)
  • 国产,中文文档友好
  • 功能丰富(权重、元数据、保护阈值)

题目:Nacos和Eureka的区别?

答案

特性EurekaNacos
一致性模型APAP + CP(可选)
健康检查客户端心跳心跳 + 主动探测
配置中心❌ 需要Config✅ 内置
权重
负载均衡RibbonRibbon或自带
维护状态❌ 停止维护✅ 活跃

推荐

  • 新项目:Nacos
  • 老项目(已用Eureka):可继续用或迁移

🎉 结束语

一周后,哈吉米把微服务架构改造完成。

哈吉米:"用Nacos后,服务扩容、配置修改都太方便了!"

南北绿豆:"对,Nacos的服务注册发现 + 配置中心,是微服务的基础设施。"

阿西噶阿西:"记住:临时实例用AP模式,持久化实例用CP模式,根据场景选择。"

哈吉米:"还有配置热更新,改配置不用重启服务,太爽了!"

南北绿豆:"对,理解了Nacos,就理解了微服务治理的核心!"


记忆口诀

Nacos服务注册发现,配置中心二合一
临时实例AP模式,持久实例CP模式
心跳保活自动摘除,配置热更新不重启
Namespace环境隔离,Group项目隔离
健康检查保高可用,版本管理可回滚