关键词:Nacos, Spring Cloud Alibaba, 微服务, 注册中心, 配置中心, 源码解析


写在前面

微服务架构已成为现代分布式系统的主流选择,而服务注册与发现配置管理是微服务架构中的核心组件。在 Spring Cloud Netflix 逐渐进入维护模式后,Spring Cloud Alibaba Nacos 凭借其强大的功能和优秀的性能,迅速成为微服务架构的首选解决方案。

本文将从 基础使用源码解析面试题 三个维度,全方位深度解析 Nacos,帮助你彻底掌握这一微服务神器。无论你是初学者还是进阶开发者,都能在本文中找到有价值的内容。


目录


一、微服务架构与 Nacos 简介

1.1 什么是微服务

微服务是由 Martin Fowler(马丁·福勒)于 2014 年提出的架构概念。微服务架构将单一应用程序拆分成一组小的服务,每个服务运行在自己的进程中,服务之间通过轻量级的通信机制(通常是 HTTP API)进行交互。

微服务的核心特点

  • 服务独立部署和扩展
  • 服务间松耦合
  • 技术栈多样化
  • 围绕业务能力组织团队

1.2 架构演变历程

互联网系统架构经历了以下演变过程:

单体应用架构

将所有功能集中在一个应用中,统一部署。

  • 优点:架构简单,适合用户量少的项目
  • 缺点:模块紧耦合,单点容错率低
垂直应用架构

将单体应用拆分为多个独立应用。

  • 优点:实现流量分担,可针对性优化
  • 缺点:系统间无法相互调用,功能冗余
分布式架构

抽取公共功能作为服务层,提高代码复用。

  • 优点:代码复用性高
  • 缺点:调用关系复杂,难以维护
SOA 架构

引入服务治理中心(ESB/Dubbo)解决调用关系问题。

  • 优点:自动调节服务间调用关系
  • 缺点:服务依赖复杂,运维困难
微服务架构

彻底拆分服务,强调独立部署、互不影响。

  • 优点:原子化拆分,利于扩展
  • 缺点:技术成本高(容错、分布式事务等)

1.3 Spring Cloud Alibaba 生态

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案,主要功能包括:

组件 功能
Nacos 服务注册发现与配置管理
Sentinel 流量控制、熔断降级
RocketMQ 分布式消息系统
Dubbo 高性能 RPC 框架
Seata 分布式事务解决方案

版本说明(课程使用版本):

  • Spring Cloud: 2020.0.1
  • Spring Cloud Alibaba: 2021.1
  • Spring Boot: 2.4.2
  • Nacos Server: 2.2.3

二、Nacos 基础实战

2.1 Nacos 简介与核心概念

Nacos(Naming Configuration Service)是一个易于使用的动态服务发现配置管理服务管理平台。

核心功能

  1. 服务注册中心 - 替代 Eureka
  2. 配置中心 - 替代 Spring Cloud Config

支持的服务类型

  • Kubernetes Service
  • gRPC & Dubbo RPC Service
  • Spring Cloud RESTful Service
为什么需要注册中心?

以酒店前台为例:

  • 没有前台:客人需要逐个房间寻找,效率低下
  • 有前台:可入住房间登记在前台,客人快速查询入住

服务提供者(酒店房间)在注册中心(前台)登记,服务消费者(客人)通过注册中心快速发现服务。

2.2 Nacos 安装与启动

下载安装

官网地址:https://nacos.io/zh-cn/index.html

GitHub 下载:https://github.com/alibaba/nacos/releases

启动命令

Linux/Unix/Mac

sh startup.sh -m standalone

Windows

startup.cmd -m standalone
访问控制台

启动成功后访问:http://localhost:8848/nacos

默认账号密码:nacos/nacos

持久化配置

Nacos 默认使用嵌入式 Derby 数据库,生产环境建议切换为 MySQL:

  1. 修改 application.properties
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8
db.user.0=root
db.password.0=root
  1. 执行 conf/nacos-mysql.sql 初始化数据库

  2. 重启服务

2.3 服务注册与发现

创建父项目
<properties>
    <spring-cloud-alibaba-version>2021.1</spring-cloud-alibaba-version>
    <spring-cloud.version>2020.0.1</spring-cloud.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba-version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
服务提供者

添加依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

配置文件(application.yml):

server:
  port: 9001
spring:
  application:
    name: nacos-provider
  cloud:
    discovery:
      server-addr: 127.0.0.1:8848

启动类

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

提供接口

@RestController
public class DemoController {
    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/hello")
    public String hello() {
        return "Hello Nacos Discovery " + serverPort;
    }
}
服务消费者

配置文件

server:
  port: 9101
spring:
  application:
    name: nacos-consumer
  cloud:
    discovery:
      server-addr: localhost:8848
service-url:
  nacos-user-service: http://nacos-provider

RestTemplate 配置

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();
}

调用服务

@RestController
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;

    @Value("${service-url.nacos-user-service}")
    private String serverURL;

    @GetMapping("/consumer/hello")
    public String hello() {
        return restTemplate.getForObject(serverURL + "/hello", String.class);
    }
}
OpenFeign 服务调用

添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

创建 Feign 接口

@FeignClient(name = "nacos-provider")
public interface ProviderService {
    @GetMapping("/hello")
    String hello();
}

启动类添加注解

@EnableFeignClients(basePackages = "com.example.feign")
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}

使用 Feign 调用

@RestController
public class ConsumerController {
    @Autowired
    private ProviderService providerService;

    @GetMapping("/consumer/feign")
    public String helloByFeign() {
        return providerService.hello();
    }
}

2.4 配置中心使用

添加依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
配置文件

bootstrap.yml(高优先级):

server:
  port: 3377
spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        file-extension: yaml

application.yml

spring:
  profiles:
    active: dev  # 开发环境
Data ID 命名规则
${prefix}-${spring.profiles.active}.${file-extension}

例如:nacos-config-client-dev.yaml

动态配置刷新
@RestController
@RefreshScope  // 支持 Nacos 动态刷新
public class ConfigController {
    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/config/info")
    public String getConfigInfo() {
        return configInfo;
    }
}
多环境配置管理

命名空间(Namespace):用于环境隔离(dev/test/prod)

配置分组(Group):用于分类管理配置(DB_GROUP/MYBATIS_GROUP)

Data ID:具体的配置文件标识

三者关系:Namespace > Group > Data ID

2.5 集群搭建

集群结构
  • 3 个 Nacos 注册中心节点
  • 1 个 MySQL 数据库
集群配置
  1. 配置 MySQL 持久化(同单机版)

  2. 配置集群节点(cluster.conf):

192.168.1.111:8848
192.168.1.111:8868
192.168.1.111:8888
  1. Nginx 负载均衡配置
upstream nacos {
    server 192.168.1.111:8848;
    server 192.168.1.111:8868;
    server 192.168.1.111:8888;
}

server {
    listen 80;
    server_name 192.168.1.111;

    location / {
        proxy_pass http://nacos;
    }
}

三、Nacos 源码深度解析

3.1 服务注册原理

客户端注册流程

入口NacosServiceRegistryAutoConfiguration

核心方法NacosServiceRegistry.register()

public void register(Registration registration) {
    // 获取 NamingService
    NamingService namingService = this.namingService();
    String serviceId = registration.getServiceId();
    String group = this.nacosDiscoveryProperties.getGroup();
    
    // 封装实例信息
    Instance instance = this.getNacosInstanceFromRegistration(registration);
    
    // 执行注册
    namingService.registerInstance(serviceId, group, instance);
}

实例信息封装

private Instance getNacosInstanceFromRegistration(Registration registration) {
    Instance instance = new Instance();
    instance.setIp(registration.getHost());
    instance.setPort(registration.getPort());
    instance.setWeight(nacosDiscoveryProperties.getWeight());
    instance.setClusterName(nacosDiscoveryProperties.getClusterName());
    instance.setEphemeral(nacosDiscoveryProperties.isEphemeral());
    return instance;
}

发送注册请求

public void registerInstance(String serviceName, String groupName, Instance instance) {
    // 临时实例开启心跳定时任务
    if (instance.isEphemeral()) {
        BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
        beatReactor.addBeatInfo(groupedServiceName, beatInfo);
    }
    // 发送 HTTP 注册请求:/nacos/v1/ns/instance
    serverProxy.registerService(groupedServiceName, groupName, instance);
}
服务端注册处理

接口InstanceController.register()

处理流程

  1. 获取 namespaceId、serviceName、instance 信息
  2. 创建客户端连接(createIpPortClientIfAbsent)
  3. 执行注册(clientOperationService.registerInstance)
  4. 发布注册事件(NotifyCenter.publishEvent)
  5. 存储到 publisherIndexes(ConcurrentHashMap)
// 注册服务存储结构
private final ConcurrentMap<Service, Set<String>> publisherIndexes = new ConcurrentHashMap<>();

private void addPublisherIndexes(Service service, String clientId) {
    publisherIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>());
    publisherIndexes.get(service).add(clientId);
    // 发布服务变更事件
    NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
}

3.2 心跳机制与健康检查

客户端心跳发送

临时实例启动定时任务发送心跳:

public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
    String key = buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());
    dom2Beat.put(key, beatInfo);
    // 开启定时任务,默认间隔 15 秒
    executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS);
}

心跳任务

@Override
public void run() {
    // 发送心跳请求
    JsonNode result = serverProxy.sendBeat(beatInfo, lightBeatEnabled);
    
    // 如果服务不存在,重新注册
    int code = result.get(CommonParams.CODE).asInt();
    if (code == NamingResponseCode.RESOURCE_NOT_FOUND) {
        Instance instance = new Instance();
        // ... 设置实例信息
        serverProxy.registerService(beatInfo.getServiceName(), groupName, instance);
    }
    
    // 继续下一次心跳
    executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
}
服务端心跳处理

接口InstanceController.beat()

核心逻辑

public int handleBeat(String namespaceId, String serviceName, String ip, int port, 
                      String cluster, RsInfo clientBeat, BeatInfoInstanceBuilder builder) {
    // 获取客户端
    String clientId = IpPortBasedClient.getClientId(ip + InternetAddressUtil.IP_PORT_SPLITER + port, true);
    IpPortBasedClient client = (IpPortBasedClient) clientManager.getClient(clientId);
    
    // 如果客户端不存在,基于心跳信息创建
    if (null == client) {
        Instance instance = builder.setBeatInfo(clientBeat).build();
        registerInstance(namespaceId, serviceName, instance);
    }
    
    // 处理心跳信息
    ClientBeatProcessorV2 beatProcessor = new ClientBeatProcessorV2(namespaceId, clientBeat, client);
    HealthCheckReactor.scheduleNow(beatProcessor);
    
    return NamingResponseCode.OK;
}

心跳处理器

@Override
public void run() {
    HealthCheckInstancePublishInfo instance = (HealthCheckInstancePublishInfo) client
            .getInstancePublishInfo(service);
    
    // 检查服务一致性
    if (instance.getIp().equals(ip) && instance.getPort() == port) {
        // 更新最后心跳时间
        instance.setLastHeartBeatTime(System.currentTimeMillis());
        
        // 如果服务不健康,更新为健康状态
        if (!instance.isHealthy()) {
            instance.setHealthy(true);
            // 发布服务状态变更事件
            NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service));
        }
    }
}
服务端健康检查

临时实例检查:每 5 秒执行一次

public void init() {
    if (ephemeral) {
        // 临时实例:客户端心跳超时检测
        beatCheckTask = new ClientBeatCheckTaskV2(this);
        HealthCheckReactor.scheduleCheck(beatCheckTask);
    } else {
        // 持久实例:服务端主动探测
        healthCheckTaskV2 = new HealthCheckTaskV2(this);
        HealthCheckReactor.scheduleCheck(healthCheckTaskV2);
    }
}

健康检查逻辑

private boolean isUnhealthy(Service service, HealthCheckInstancePublishInfo instance) {
    // 获取超时时间,默认 15 秒
    long beatTimeout = getTimeout(service, instance);
    // 超过超时时间则认为不健康
    return System.currentTimeMillis() - instance.getLastHeartBeatTime() > beatTimeout;
}

持久实例检查:通过 HTTP 请求主动探测

@Override
public void process(HealthCheckTaskV2 task, Service service, ClusterMetadata metadata) {
    // 拼接探测地址
    URL target = new URL(HTTP_PREFIX + instance.getIp() + ":" + ckPort + healthChecker.getPath());
    
    // 发送 HTTP 健康检查请求
    ASYNC_REST_TEMPLATE.get(target.toString(), header, Query.EMPTY, String.class,
            new HttpHealthCheckCallback(instance, task, service));
}

3.3 服务订阅与发现

客户端订阅流程

入口NacosDiscoveryClient.getInstances()

public List<ServiceInstance> getInstances(String serviceId) throws NacosException {
    String group = this.discoveryProperties.getGroup();
    // 获取服务实例列表
    List<Instance> instances = this.namingService().selectInstances(serviceId, group, true);
    return hostToServiceInstanceList(instances, serviceId);
}

获取服务信息

public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {
    // 1. 从缓存获取
    ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);
    
    if (null == serviceObj) {
        // 2. 缓存没有,从服务端获取
        updateServiceNow(serviceName, clusters);
    }
    
    // 3. 开启定时更新任务
    scheduleUpdateIfAbsent(serviceName, clusters);
    
    return serviceInfoMap.get(serviceObj.getKey());
}
服务端处理订阅

接口InstanceController.list()

@GetMapping("/list")
public Object list(HttpServletRequest request) throws Exception {
    // 创建订阅者对象
    Subscriber subscriber = new Subscriber(clientIP + ":" + udpPort, agent, app, 
                                           clientIP, namespaceId, serviceName, udpPort, clusters);
    // 获取服务列表
    return getInstanceOperator().listInstance(namespaceId, serviceName, subscriber, clusters, healthyOnly);
}

订阅处理

@Override
public ServiceInfo listInstance(String namespaceId, String serviceName, Subscriber subscriber, 
                                 String cluster, boolean healthOnly) {
    Service service = getService(namespaceId, serviceName, true);
    
    // 如果是订阅请求,建立订阅关系
    if (subscriber.getPort() > 0 && pushService.canEnablePush(subscriber.getAgent())) {
        clientOperationService.subscribeService(service, subscriber, clientId);
    }
    
    // 获取服务信息
    ServiceInfo serviceInfo = serviceStorage.getData(service);
    return ServiceUtil.selectInstancesWithHealthyProtection(serviceInfo, serviceMetadata, 
                                                            cluster, healthOnly, true, subscriber.getIp());
}
服务变更推送

UDP 推送:Nacos 1.x 使用 UDP 主动推送服务变更

@Override
public void onEvent(Event event) {
    if (event instanceof ServiceEvent.ServiceChangedEvent) {
        ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event;
        Service service = serviceChangedEvent.getService();
        // 延迟推送任务
        delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay()));
    }
}

3.4 配置中心原理

客户端获取配置

入口NacosConfigService.getConfig()

private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) {
    // 1. 优先从本地缓存获取
    String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);
    if (content != null) {
        return content;
    }
    
    try {
        // 2. 从服务端获取
        String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
        return ct[0];
    } catch (NacosException ioe) {
        // 3. 服务端失败,从本地快照获取
        content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant);
        return content;
    }
}

服务端查询ConfigController.getConfig()

长轮询机制

Nacos 使用长轮询(Long Polling)实现配置的实时更新:

客户端

// 检查配置更新
List<String> changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList);

for (String groupKey : changedGroupKeys) {
    // 获取变更的配置内容
    String content = getServerConfig(dataId, group, tenant, 3000L);
    // 更新本地缓存
    cache.setContent(content);
}

// 继续下一次轮询
executorService.execute(this);

长轮询请求

List<String> checkUpdateConfigStr(String probeUpdateString, boolean isInitializingCacheList) {
    List<String> headers = new ArrayList<>();
    headers.add("Long-Pulling-Timeout");
    headers.add("" + timeout);  // 默认 30 秒
    
    // 发送长轮询请求
    HttpResult result = agent.httpPost(Constants.CONFIG_CONTROLLER_PATH + "/listener", 
                                       headers, params, agent.getEncode(), timeout);
    
    if (HttpURLConnection.HTTP_OK == result.code) {
        return parseUpdateDataIdResponse(result.content);
    }
    return Collections.emptyList();
}

服务端处理ConfigServletInner.doPollingConfig()

public String doPollingConfig(HttpServletRequest request, HttpServletResponse response,
                              Map<String, String> clientMd5Map, int probeRequestSize) {
    if (LongPollingService.isSupportLongPolling(request)) {
        // 添加长轮询客户端
        longPollingService.addLongPollingClient(request, response, clientMd5Map, probeRequestSize);
        return HttpServletResponse.SC_OK + "";
    }
    // ... 兼容短轮询逻辑
}

长轮询客户端

public void addLongPollingClient(HttpServletRequest req, HttpServletResponse rsp, 
                                 Map<String, String> clientMd5Map, int probeRequestSize) {
    // 提前 500ms 返回,避免客户端超时
    long timeout = Math.max(10000, Long.parseLong(str) - delayTime);
    
    // 先检查是否有变更
    List<String> changedGroups = MD5Util.compareMd5(req, rsp, clientMd5Map);
    if (changedGroups.size() > 0) {
        generateResponse(req, rsp, changedGroups);
        return;
    }
    
    // 无变更,开启异步处理
    final AsyncContext asyncContext = req.startAsync();
    asyncContext.setTimeout(0L);
    
    // 提交长轮询任务
    ConfigExecutor.executeLongPolling(
        new ClientLongPolling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag));
}

配置变更通知

@Override
public void onEvent(Event event) {
    if (event instanceof LocalDataChangeEvent) {
        LocalDataChangeEvent evt = (LocalDataChangeEvent) event;
        // 执行数据变更任务
        scheduler.execute(new DataChangeTask(evt.groupKey, evt.isBeta, evt.betaIps));
    }
}

3.5 Nacos 2.x 架构升级

Nacos 1.x 问题
  1. 心跳数量多,TPS 高 - 大量心跳和元数据轮询
  2. 心跳感知变化慢 - 默认 15 秒超时,时效性差
  3. UDP 推送不可靠 - 客户端需要频繁对账查询
  4. HTTP 短连接问题 - 大量 TIME_WAIT 状态连接
  5. 30 秒长轮询 GC 问题 - 频繁的上下文切换和内存浪费
Nacos 2.x 改进

基于 gRPC 的长连接架构

特性 Nacos 1.x Nacos 2.x
协议 HTTP 1.1 gRPC (HTTP/2)
连接 短连接 长连接
心跳 客户端定时发送 长连接 keepalive
推送 UDP 双向流推送
性能 较低 提升 10 倍

优点

  • 降低 TPS:不再需要频繁心跳
  • 快速感知:TCP 连接断开立即感知
  • 可靠推送:基于 HTTP/2 的双向流
  • 减少连接:长连接避免 TIME_WAIT
  • 降低 GC:真实长连接解决配置模块 GC 问题

客户端源码核心

// NamingGrpcClientProxy - gRPC 客户端代理
public class NamingGrpcClientProxy extends NamingClientProxy {
    private final RpcClient rpcClient;
    
    @Override
    public void registerService(String serviceName, String groupName, Instance instance) {
        // 构建 gRPC 请求
        InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName, 
                                                      NamingRequestType.REGISTER_INSTANCE, instance);
        // 发送 gRPC 请求
        requestToServer(request, Response.class);
    }
}

四、Nacos 面试题精选

Q1: Nacos 为什么选择 gRPC?

  1. HTTP 1.x 短板:无多路复用、Header 冗余、队头阻塞
  2. Nacos 1.x 影响:高 TPS、大量连接、UDP 不可靠
  3. gRPC 优势
    • HTTP/2 多路复用
    • Header 压缩(HPACK)
    • Protobuf 高效序列化
    • 长连接双向流

Q2: Nacos 中的保护阈值是什么?

保护阈值(0-1)用于防止服务雪崩

  • 设置阈值为 0.5
  • 当健康实例少于 50% 时
  • 消费者会拉取所有实例(包括不健康)
  • 保证至少部分请求能正常处理

Q3: Nacos 的负载均衡是如何工作的?

Nacos 提供服务端负载均衡

  • 消费者拉取服务列表时
  • Nacos 可根据随机策略返回部分实例
  • 减少网络传输和客户端内存占用
  • Ribbon/LoadBalancer 在其基础上进行二次负载均衡

Q4: Nacos 的就近访问是什么?

基于 Cluster Name 的就近路由:

  • 给实例设置 cluster-name(如:beijing/shanghai)
  • 消费者优先调用同集群的服务实例
  • 减少跨机房/跨地域的网络延迟

Q5: Nacos 保证 CP 还是 AP?

Nacos 支持CP 和 AP 切换

  • 临时实例(默认):AP 模式,使用 Distro 协议
  • 持久实例:CP 模式,使用 Raft 协议
  • 配置中心:数据存储在 MySQL,不涉及 CAP
# 切换为 CP 模式
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'

Q6: Nacos 如何判断服务实例状态?

  1. 心跳检测(临时实例):

    • 客户端每 15 秒发送心跳
    • 服务端超过 15 秒未收到标记为不健康
    • 超过 30 秒移除实例
  2. 健康检查(持久实例):

    • 服务端主动发送 HTTP/TCP 探测
    • 根据返回结果判断健康状态
  3. 负载均衡

    • 根据实例负载情况动态调整

Q7: Nacos 1.x 注册中心架构流程

┌─────────────┐     1.服务注册      ┌─────────────┐
│   Provider  │ ─────────────────> │    Nacos    │
└─────────────┘                    └──────┬──────┘
       │                                  │
       │ 5.心跳(每5秒)                    │ 6.健康检查(每5秒)
       │                                  │
       └────────────────────────────────> │
                                          │
┌─────────────┐     2.服务发现      ┌─────┴──────┐
│   Consumer  │ ─────────────────> │            │
└─────────────┘                    │            │
       │                           │            │
       │ 3.定时拉取(每10秒)         │            │
       │                           │            │
       │ <──────────────────────── │            │
       │                           │            │
       │ 4.UDP推送(服务变更)        │            │
       │ <──────────────────────── │            │
                                   └────────────┘

Q8: Nacos 中的 Distro 协议

Distro 是 Nacos 自研的 AP 分布式协议

设计思想:

  1. 每个节点平等:都可处理写请求,同步数据到其他节点
  2. 数据分片:每个节点负责部分数据
  3. 定时校验:发送校验值保持数据一致性
  4. 独立读请求:从本地直接响应

五、总结与展望

本文从基础使用源码解析面试题三个维度全面解析了 Spring Cloud Alibaba Nacos

核心知识点回顾

  1. 服务注册与发现

    • 客户端通过 NacosServiceRegistry 注册服务
    • 服务端存储在 publisherIndexes(ConcurrentHashMap)
    • 支持临时实例(AP)和持久实例(CP)
  2. 心跳与健康检查

    • 临时实例:客户端定时发送心跳(15秒)
    • 持久实例:服务端主动探测(HTTP/TCP)
    • 超时机制:15秒标记不健康,30秒移除
  3. 配置中心

    • 本地缓存优先,服务端兜底
    • 长轮询机制实现实时更新(30秒超时)
    • Data ID 命名规则:${prefix}-${profile}.${extension}
  4. 架构演进

    • Nacos 1.x:HTTP 短连接 + UDP 推送
    • Nacos 2.x:gRPC 长连接 + 双向流推送
    • 性能提升 10 倍

学习建议

  1. 动手实践:搭建 Nacos 集群,实现服务注册、配置管理
  2. 阅读源码:重点阅读 nacos-namingnacos-config 模块
  3. 关注面试题:理解 CAP 原理、心跳机制、长轮询等核心概念
  4. 跟进版本:Nacos 2.x 是趋势,建议优先学习

相关资源

  • 官方文档:https://nacos.io/zh-cn/docs/what-is-nacos.html
  • GitHub:https://github.com/alibaba/nacos
  • Spring Cloud Alibaba:https://github.com/alibaba/spring-cloud-alibaba

如果本文对你有帮助,欢迎点赞、收藏、关注!如有疑问,欢迎在评论区留言讨论。

关键词:Nacos, Spring Cloud Alibaba, 微服务, 注册中心, 配置中心, 源码解析, Java, 分布式系统


本文基于 Nacos 2.2.3 + Spring Cloud Alibaba 2021.1 整理,持续更新中…

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐