Skip to content

Nacos — 服务发现与配置中心

架构概览

Nacos(Naming and Configuration Service)是阿里巴巴开源的服务发现与配置管理平台,一站式解决微服务的注册发现和配置管理需求。

┌─────────────────────────────────────────────────────┐
│                   Nacos Server                       │
│  ┌──────────────┐    ┌──────────────────────────┐   │
│  │  服务注册发现  │    │       配置管理            │   │
│  │  (AP/CP可切换)│    │  (命名空间/Group/DataId)  │   │
│  └──────────────┘    └──────────────────────────┘   │
│  ┌──────────────────────────────────────────────┐   │
│  │           一致性协议层                         │   │
│  │   AP: Distro(自研,类 Gossip)                │   │
│  │   CP: Raft(JRaft 实现)                      │   │
│  └──────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────┘

AP vs CP 模式

模式协议适用场景
AP(默认)Distro服务注册发现,允许短暂不一致,优先可用性
CPRaft配置管理、需要强一致性的服务注册
bash
# 注册服务时指定 ephemeral=false 使用 CP 模式
# ephemeral=true(默认)→ AP 模式,临时实例
# ephemeral=false → CP 模式,持久化实例

服务注册与发现

Spring Cloud 集成

xml
<!-- pom.xml -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>2023.0.1.0</version>
</dependency>
yaml
# application.yml
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        namespace: dev          # 命名空间隔离
        group: DEFAULT_GROUP
        cluster-name: BJ        # 集群名,就近访问
        metadata:
          version: v1.2.0
          env: production

健康检查机制

临时实例(ephemeral=true):
  客户端心跳 → Nacos Server(默认 5s 一次)
  15s 未收到心跳 → 标记为不健康
  30s 未收到心跳 → 删除实例

持久化实例(ephemeral=false):
  Nacos Server 主动探测(HTTP/TCP/MySQL)
  探测失败 → 标记为不健康,但不删除

服务发现与负载均衡

java
// 使用 LoadBalancerClient
@Autowired
private LoadBalancerClient loadBalancer;

public String callOrderService() {
    ServiceInstance instance = loadBalancer.choose("order-service");
    String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/api/orders";
    return restTemplate.getForObject(url, String.class);
}

// 使用 @LoadBalanced RestTemplate(推荐)
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();
}

// 直接使用服务名
restTemplate.getForObject("http://order-service/api/orders", String.class);

配置管理

配置模型

Namespace(命名空间)→ 环境隔离(dev/test/prod)
  └── Group(分组)→ 应用分组
        └── DataId(配置 ID)→ 具体配置文件
              格式:${spring.application.name}-${spring.profiles.active}.${file-extension}
              示例:order-service-prod.yaml

Spring Cloud Config 集成

xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
yaml
# bootstrap.yml(必须用 bootstrap,优先于 application)
spring:
  application:
    name: order-service
  profiles:
    active: prod
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        namespace: prod-namespace-id
        group: ORDER_GROUP
        file-extension: yaml
        # 共享配置(多服务共用)
        shared-configs:
          - data-id: common-db.yaml
            group: COMMON_GROUP
            refresh: true
        # 扩展配置(优先级高于 shared-configs)
        extension-configs:
          - data-id: order-service-extra.yaml
            group: ORDER_GROUP
            refresh: true

配置热更新

java
@RestController
@RefreshScope  // 标注此注解,配置变更时自动刷新
public class OrderController {
    
    @Value("${order.timeout:5000}")
    private int timeout;
    
    @Value("${order.max-retry:3}")
    private int maxRetry;
}

// 或使用 @ConfigurationProperties(推荐,类型安全)
@Component
@ConfigurationProperties(prefix = "order")
@RefreshScope
@Data
public class OrderProperties {
    private int timeout = 5000;
    private int maxRetry = 3;
    private String paymentUrl;
}

监听配置变更

java
@Autowired
private NacosConfigManager configManager;

@PostConstruct
public void listenConfig() throws NacosException {
    configManager.getConfigService().addListener(
        "order-service-prod.yaml",
        "ORDER_GROUP",
        new Listener() {
            @Override
            public void receiveConfigInfo(String configInfo) {
                log.info("Config changed: {}", configInfo);
                // 手动处理配置变更逻辑
            }
            
            @Override
            public Executor getExecutor() {
                return null;  // null 表示使用默认线程池
            }
        }
    );
}

集群部署

集群模式配置

bash
# cluster.conf(所有节点相同)
192.168.1.10:8848
192.168.1.11:8848
192.168.1.12:8848
properties
# application.properties
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://mysql:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user.0=nacos
db.password.0=nacos_password

数据持久化

Nacos 集群必须配置外部 MySQL 存储配置数据(服务注册数据存内存+磁盘):

sql
-- 初始化 Nacos 数据库
source /nacos/conf/mysql-schema.sql;

命名空间隔离最佳实践

推荐方案:按环境隔离
  ├── dev(开发环境)
  ├── test(测试环境)
  ├── staging(预发布)
  └── prod(生产环境)

不推荐:按服务隔离(命名空间过多,管理复杂)

故障处理案例

案例一:服务注册后无法被发现

排查步骤

bash
# 1. 检查服务是否注册成功
curl "http://nacos:8848/nacos/v1/ns/instance/list?serviceName=order-service&namespaceId=dev"

# 2. 检查健康状态
curl "http://nacos:8848/nacos/v1/ns/instance?serviceName=order-service&ip=192.168.1.100&port=8080"

# 3. 检查命名空间和 Group 是否匹配

常见原因

  • 消费者和提供者的 namespace / group 不一致
  • 服务名大小写不匹配
  • 网络隔离导致心跳无法到达

案例二:配置不生效

排查

bash
# 检查配置是否发布
curl "http://nacos:8848/nacos/v1/cs/configs?dataId=order-service-prod.yaml&group=ORDER_GROUP&tenant=prod-ns-id"

# 检查客户端是否收到推送(查看日志)
grep "config changed" application.log

常见原因

  • @RefreshScope 未添加
  • bootstrap.yml 中 namespace 填写的是名称而非 ID
  • 配置文件扩展名不匹配(file-extension: yaml 但 DataId 是 .properties

案例三:Nacos 集群脑裂

现象:网络分区后,各节点数据不一致。

处理

bash
# 查看集群状态
curl http://nacos:8848/nacos/v1/core/cluster/nodes

# 查看 Raft 状态(CP 模式)
curl http://nacos:8848/nacos/v1/core/cluster/raft/leader

Nacos 集群建议部署奇数节点(3或5),并确保节点间网络稳定。

监控指标

bash
# Nacos 暴露 Prometheus 指标
curl http://nacos:8848/nacos/actuator/prometheus
指标说明
nacos_monitor_service_count注册服务数
nacos_monitor_instance_count注册实例数
nacos_monitor_config_count配置数量
nacos_monitor_notify_task_count配置推送任务数

PaaS 中间件生态系统深度学习文档