Nacos注册中心原理
一、核心架构图示
二、关键技术流程详解
1. 服务注册阶段
- 实现原理:服务启动时将元数据(服务名、实例ID、端点地址)封装成instance对象向注册中心发起POST请求
- 关键参数:
# 示例配置(Spring Boot)
spring:
cloud:
inetutils:
preferred-networks: 192.168.1.0/24 # IP过滤规则
instance-id: ${spring.application.name}:${server.port}
1.1 注册存储
在Nacos Server(注册中心)接收到请求后:
1)将服务实例信息持久化到内存数据库(基于Raft协议的nacos/nacos-cluster模块)
2)同步更新到本地文件系统(snapshot机制)
3)构建服务实例索引(按服务名+分组归类)
2. 服务发现阶段
- 订阅机制:
- 长连接:WebSocket保持实时通信
- 短轮询:HTTP GET /services/{serviceName} 定期拉取
- 缓存策略:
// 客户端缓存实现示例
public class ServiceCache {
private Map<String, List<ServiceInstance>> cache = new ConcurrentHashMap<>();@Scheduled(fixedDelay = 30000)
public void refreshCache() {
cache.putAll(registryClient.getServices());}
}
3. 心跳续约机制
服务提供者在注册中心完成注册之后与注册中心会建立长连接:
1)服务实例每5秒发送一次心跳(默认间隔)
2)注册中心通过TCP长连接(默认端口7848)接收心跳
3)心跳超时时间默认15秒,超时实例将被剔除
3.1 如何处理心跳请求?
- 注册中心接收到服务提供者的心跳请求,接着解析请求中的Instance对象并执行以下核心操作:
(1) 实例注册校验
若服务实例不存在:立即异步注册
若已存在:更新最后心跳时间
(2) 状态维护
自动恢复非健康实例为健康状态
(3) 异步处理
启动独立线程执行注册/更新操作,避免阻塞主流程
- 注册中心注册表维护机制
(1)注册表的数据结构
使用ConcurrentHashMap分级存储:Map<命名空间, Map<分组:服务名, Service对象>>
(2)持久化策略
模式 | 协议 | 触发条件 |
CP模式 | 基于Raft协议 |
|
AP模式 | 基于Distro协议 |
|
4. 负载均衡策略
以下是 Nacos 客户端(服务调用者)常用的负载均衡策略对比表:
策略名称 | 核心原理 | 适用场景 | 优点 | 缺点 | 配置示例 |
---|---|---|---|---|---|
轮询(Round Robin) | 按顺序依次选择实例(如实例列表为 [A,B,C],依次选择 A→B→C→A...) | 实例性能相近的简单场景 | 实现简单,请求均匀分布 | 忽略实例实际负载差异 | LoadBalancerClient.setRule(new RoundRobinRule()) |
随机(Random) | 从实例列表中随机选择一个实例 | 实例性能差异不大的高并发场景 | 无状态,实现简单 | 可能导致某些实例过载 | new RandomRule() |
加权轮询(Weighted Round Robin) | 根据实例权重分配请求(权重高的实例被选中概率更高) | 实例配置差异明显的场景(如不同规格服务器) | 灵活应对实例性能差异 | 需手动配置权重 | new WeightedResponseTimeRule() |
最少连接(Least Connections) | 优先选择当前连接数最少的实例 | 长连接服务(如数据库、WebSocket) | 动态感知实例负载 | 需维护连接数状态 | new LeastConnectionsRule() |
响应时间权重(Response Time Weighted) | 根据实例平均响应时间动态计算权重(响应越快,权重越高) | 对延迟敏感的服务调用 | 自动优化用户体验 | 需持续监控响应时间 | new BestAvailableRule() |
一致性哈希(Consistent Hashing) | 通过哈希环将请求映射到固定实例,相同参数的请求总是路由到同一实例(会话粘滞) | 需保持会话状态的服务(如购物车) | 避免缓存失效,减少热点问题 | 实例扩缩容时哈希抖动 | new ConsistentHashRule() |
补充说明
- 默认策略:Nacos 默认使用 加权轮询(结合实例健康状态和响应时间动态调整权重)。
- 扩展性:可通过实现
IRule
接口自定义策略(如基于 CPU/内存利用率的动态权重算法)。 - 配置方式:在 Spring Cloud 中可通过
application.yml
配置:spring:cloud:loadbalancer:ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
选择建议:中小规模服务优先使用默认加权轮询;对延迟敏感场景选择响应时间权重;需要会话粘滞时选一致性哈希。
三、技术选型对比矩阵
特性 | Consul | Eureka | Nacos |
---|---|---|---|
协议支持 | HTTP/gRPC | HTTP | HTTP/gRPC |
一致性模型 | CP | AP | CP+AP |
配置中心 | 内置 | 需独立部署 | 内置 |
部署复杂度 | 中等(需etcd) | 简单(纯Java) | 简单(Spring Cloud集成) |
实施建议:中小规模系统推荐使用Nacos(简化运维),金融级系统建议Consul+Istio组合(强一致性+服务网格)。关键业务系统务必配置健康检查熔断机制,建议心跳超时时间设置为30s-60s,重试次数不超过3次。
四、思考
此时你的服务提供者其中有一个服务挂掉了,这时候其注册中心和调用者会怎么做呢?
情况一:服务提供者在销毁时间前恢复
1. 服务提供者动作
- 重启并恢复心跳:服务提供者进程重启后,重新向 Nacos Server 发送心跳请求(PUT 接口),携带自身实例信息(IP、端口、服务名等)。
2. 注册中心(Nacos Server)动作
- 接收心跳请求:Nacos Server 收到心跳后,检查实例状态:
- 若实例此前被标记为 非健康,则将其状态更新为 健康。
- 若实例此前已被 注销(超过销毁时间),则忽略此次心跳(视为无效恢复)。
- 触发健康状态更新事件:通过 UDP 通信 向所有订阅该服务的客户端广播服务实例状态变更事件(如
InstanceStatusChangedEvent
)。 - 持久化存储更新:若启用 CP 模式(Raft 协议),更新 Raft 日志并同步到集群半数以上节点;若为 AP 模式(Distro 协议),异步更新内存并广播增量数据。
3. 调用者动作
-
接收事件通知:客户端通过 UDP 监听收到实例状态变更事件(如
InstanceStatusChangedEvent
)。 - 更新本地缓存:将恢复健康的实例重新加入负载均衡候选池。
- 后续调用逻辑:下次发起请求时,负载均衡器(如 Ribbon)会基于最新实例列表选择健康实例(包括刚恢复的实例)。
情况二:服务提供者在销毁时间后恢复
1. 服务提供者动作
- 超时后重启:服务提供者因宕机超过销毁时间(如 Nacos 配置的
ephemeralTTL
或默认 30 秒),实例已被注册中心移除。 - 重新注册:进程重启后,向 Nacos Server 发送 POST 请求 触发全新注册(而非恢复旧实例)。
2. 注册中心(Nacos Server)动作
- 处理新注册请求:
- 将实例信息封装为新的
Instance
对象。 - 创建异步任务写入注册表(CP 模式需 Raft 半数节点确认,AP 模式直接写入内存并向集群广播)。
- 推送全量服务列表:通过 UDP 向客户端(调用者)广播服务实例变更事件(如
ServiceChangeEvent
),通知所有订阅者该服务实例已重新加入集群。
3. 调用者动作
- 接收全量更新事件:客户端收到事件后,触发 本地缓存刷新(通过定时拉取或 UDP 事件驱动)。
- 移除旧实例:本地缓存中已不存在超时的旧实例,新注册的实例作为独立实体加入服务列表。
- 负载均衡调整:后续调用会基于最新服务列表(包含新注册实例)进行路由。