Nacos 配置中心与服务发现深度实践

掌握 Nacos 作为动态配置中心与服务注册发现中心的核心原理,实现配置热更新、灰度发布与多环境管理

在微服务架构中,配置管理与服务发现是基础设施的核心能力。Nacos 作为阿里巴巴开源的动态服务发现、配置管理和服务管理平台,已成为 Spring Cloud Alibaba 生态的首选组件。

一、Nacos 核心架构

1.1 两大核心能力

┌─────────────────────────────────────────────┐
│                  Nacos Server                 │
├──────────────────────┬──────────────────────┤
│    配置管理 (Config)  │   服务发现 (Naming)   │
│  - 动态配置推送       │   - 服务注册            │
│  - 配置版本历史       │   - 健康检查            │
│  - 灰度发布          │   - 负载均衡            │
│  - 多环境隔离         │   - 服务元数据          │
└──────────────────────┴──────────────────────┘

1.2 数据一致性模型

模块一致性协议数据存储
配置管理Raft(CP)Derby/MySQL
服务发现Distro(AP)内存 + 持久化

Distro 协议是阿里巴巴自研的 AP 协议,专为服务发现的高可用场景设计。

二、配置中心实战

2.1 基础集成

# bootstrap.yml(或 application.yml)
spring:
  application:
    name: order-service
  profiles:
    active: dev
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        namespace: ${spring.profiles.active}
        group: DEFAULT_GROUP
        file-extension: yaml
        # 共享配置
        shared-configs:
          - data-id: common.yaml
            group: DEFAULT_GROUP
            refresh: true
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: ${spring.profiles.active}

2.2 配置热更新

@RestController
@RefreshScope  // 关键注解:支持配置动态刷新
public class OrderController {
    
    @Value("${order.timeout:5000}")
    private Integer timeout;
    
    @Value("${order.max-retry:3}")
    private Integer maxRetry;
    
    @GetMapping("/config")
    public Map<String, Object> getConfig() {
        return Map.of(
            "timeout", timeout,
            "maxRetry", maxRetry
        );
    }
}

2.3 多环境配置管理

# Nacos 控制台配置 Data ID 命名规则
# ${prefix}-${spring.profiles.active}.${file-extension}

# 示例配置列表:
# order-service.yaml          # 默认配置
# order-service-dev.yaml      # 开发环境
# order-service-test.yaml     # 测试环境  
# order-service-prod.yaml     # 生产环境
# common.yaml                 # 共享配置

2.4 灰度配置发布

@Configuration
public class GrayConfig {
    
    @NacosValue(value = "${feature.new-payment-gateway:false}", autoRefreshed = true)
    private boolean newPaymentGateway;
    
    @NacosValue(value = "${gray.user.percent:0}", autoRefreshed = true)
    private int grayPercent;
    
    public boolean enableNewGateway(String userId) {
        if (!newPaymentGateway) return false;
        // 按用户 ID hash 取模实现灰度
        int hash = Math.abs(userId.hashCode()) % 100;
        return hash < grayPercent;
    }
}

三、服务发现与负载均衡

3.1 服务注册

@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

3.2 自定义负载均衡

@Component
public class NacosLoadBalancerConfig {
    
    @Bean
    public ReactorLoadBalancer<ServiceInstance> nacosLoadBalancer(
            Environment env,
            LoadBalancerClientFactory factory) {
        String name = env.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new NacosLoadBalancer(
            factory.getLazyProvider(name, ServiceInstanceListSupplier.class),
            name
        );
    }
}

// 基于权重的自定义负载均衡
public class NacosLoadBalancer implements ReactorLoadBalancer<ServiceInstance> {
    
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return supplier.get().next().map(instances -> {
            // Nacos 实例自带权重 weight(0~1)
            List<ServiceInstance> healthy = instances.stream()
                .filter(i -> "true".equals(i.getMetadata().get("nacos.healthy")))
                .collect(Collectors.toList());
            
            // 按权重随机选择
            double totalWeight = healthy.stream()
                .mapToDouble(i -> Double.parseDouble(i.getMetadata().get("nacos.weight")))
                .sum();
            
            double random = ThreadLocalRandom.current().nextDouble(totalWeight);
            for (ServiceInstance instance : healthy) {
                random -= Double.parseDouble(instance.getMetadata().get("nacos.weight"));
                if (random <= 0) return new DefaultResponse(instance);
            }
            return new DefaultResponse(healthy.get(0));
        });
    }
}

3.3 服务元数据与版本路由

# application.yml
spring:
  cloud:
    nacos:
      discovery:
        metadata:
          version: v2          # 版本标识
          region: cn-hangzhou  # 地域标识
          protocol: http       # 协议标识
// 基于元数据的服务筛选
@FeignClient(name = "payment-service", 
             fallbackFactory = PaymentFallbackFactory.class,
             configuration = VersionFeignConfig.class)
public interface PaymentClient {
    @PostMapping("/pay")
    PaymentResult pay(@RequestBody PaymentRequest req);
}

// 只调用 v2 版本的实例
public class VersionFeignConfig {
    @Bean
    public RequestInterceptor versionInterceptor() {
        return template -> template.header("X-Target-Version", "v2");
    }
}

四、Nacos 集群与高可用

4.1 集群部署架构

# cluster.conf(三台节点)
192.168.1.101:8848
192.168.1.102:8848
192.168.1.103:8848

4.2 嵌入式 vs 外置存储

模式数据存储适用场景
单例内嵌 Derby开发测试
集群(内置)内嵌 Derby + Raft小型集群
集群(外置)MySQL 8.0生产环境
# application.properties(外置 MySQL)
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8
db.user.0=nacos
db.password.0=nacos

4.3 配置持久化与历史版本

// 监听配置变更历史
@NacosConfigListener(dataId = "order-service.yaml", groupId = "DEFAULT_GROUP")
public void onChange(String config) {
    log.info("配置已更新: {}", config);
}

// Nacos 控制台支持:配置回滚、版本对比、变更审计

五、最佳实践

5.1 配置分层设计

配置分层:
├── 基础层(common.yaml)        # 所有服务共享:数据库连接池、日志级别
├── 服务层({service}.yaml)      # 单个服务配置:业务参数、开关
├── 环境层({service}-{env}.yaml) # 环境差异:超时时间、线程池大小
└── 临时层(Nacos 控制台热改)     # 紧急调整:限流阈值、降级开关

5.2 敏感配置加密

# 加密配置(Nacos 2.x 支持 AES 加密)
order:
  db:
    password: ENC(加密后的密文)
@Configuration
public class EncryptConfig {
    
    @Bean
    public StringEncryptor stringEncryptor() {
        return new AESStringEncryptor(new SimpleAESConfig(
            System.getenv("NACOS_ENCRYPT_KEY")
        ));
    }
}

5.3 配置变更通知

@Component
public class ConfigChangeListener {
    
    @Autowired
    private ThreadPoolExecutor executor;
    
    @NacosConfigListener(dataId = "thread-pool.yaml")
    public void onThreadPoolConfigChange(String config) {
        ThreadPoolConfig cfg = YamlUtils.parse(config, ThreadPoolConfig.class);
        // 动态调整线程池参数
        executor.setCorePoolSize(cfg.getCoreSize());
        executor.setMaximumPoolSize(cfg.getMaxSize());
        executor.setKeepAliveTime(cfg.getKeepAlive(), TimeUnit.SECONDS);
    }
}

六、Nacos vs 其他注册中心

特性NacosEurekaConsulZooKeeper
配置中心内置无(需 Config Server)支持
健康检查TCP/HTTP/MYSQL客户端心跳多种协议临时节点
负载均衡权重 + 元数据RibbonFabio需自研
一致性CP + APAPCPCP
性能(QPS)10w+较低中等中等
Spring CloudAlibaba 官方Netflix(停止维护)社区需 Curator

七、常见问题

7.1 配置不生效排查

# 1. 检查客户端日志
grep -i "nacos" logs/spring.log

# 2. 确认 Data ID 格式
# 正确的:order-service-dev.yaml
# 错误的:order-service-dev.yml(后缀不匹配)

# 3. 检查 namespace/group 是否匹配

7.2 服务注册不上

// 常见原因:
// 1. @EnableDiscoveryClient 缺失
// 2. 包扫描路径错误
// 3. 网络不通(防火墙、安全组)
// 4. 元数据过大会导致注册失败(限制 32KB)

7.3 长连接与推送

# Nacos 2.x 使用 gRPC 长连接替代 HTTP 轮询
server:
  grpc:
    port: 9848  # gRPC 端口(Nacos 2.x 默认)

八、总结

能力关键配置注意事项
配置管理shared-configsnamespace注意 Data ID 命名规则
服务发现metadataweight健康检查配置合理
灰度发布动态配置 + 业务判断配置变更需幂等
集群部署MySQL 外置存储至少 3 节点保证 Raft

Nacos 将配置管理与服务发现统一,降低了微服务基础设施的复杂度。在生产环境中,建议配置外置存储、开启鉴权、做好集群规划。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「java-enterprise」更多文章

  1. 限流算法深度解析:令牌桶、漏桶与滑动窗口计数
  2. Java 代码质量:SonarQube、Checkstyle 与 SpotBugs 工程化实践
  3. Spring IoC 容器与依赖注入原理深度剖析