nacos
Nacos 本身并不会主动去发现服务,而是服务启动时主动向 Nacos 注册,消费者再从 Nacos 拉取服务列表并订阅变化,从而实现服务发现。
下面按照整个流程来介绍。
一、什么是服务注册和服务发现
假设有两个服务:
Order-Service
User-Service
Order 调用 User。
如果没有注册中心:
Order -----> http://192.168.1.5:8080
IP一旦变化:
Order 配置全部修改
非常麻烦。
有了 Nacos:
Nacos
|
-------------------
| |
User-1 User-2
Order
Order只需要问Nacos:
User-Service在哪?
Nacos返回:
User-Service
192.168.1.5:8080
192.168.1.8:8080
192.168.1.9:8080
然后Order自己选择一个去调用。
这就是服务发现(Service Discovery)。
二、服务注册(Service Register)
例如:
User-Service
启动时。
Spring Cloud Alibaba 自动完成以下事情:
ApplicationContext启动
↓
NacosAutoServiceRegistration
↓
获取本机信息
IP
PORT
服务名
↓
调用NamingService.registerInstance()
↓
HTTP请求
POST
/v1/ns/instance
↓
Nacos Server
例如:
注册的数据:
serviceName=user-service
ip=192.168.1.5
port=8081
weight=1
cluster=DEFAULT
healthy=true
Nacos收到以后,保存到内存。
例如:
user-service
|
+------192.168.1.5:8081
如果再启动一个:
192.168.1.6:8081
变成:
user-service
|
+------192.168.1.5
|
+------192.168.1.6
这就是服务注册。
三、Nacos保存在哪里?
Nacos实际上维护了一张大的注册表。
可以理解成:
Map<
ServiceName,
List<Instance>
>
例如:
{
"user-service":[
192.168.1.5:8081,
192.168.1.6:8081
],
"order-service":[
192.168.1.10:8082
]
}
实际上源码中维护的是类似:
namespace
↓
group
↓
service
↓
cluster
↓
instance
层级。
四、服务发现(Discovery)
Order启动以后:
Ribbon
OpenFeign
LoadBalancer
都会调用:
NamingService.getAllInstances("user-service");
实际上就是:
GET
/v1/ns/instance/list
Nacos返回:
[
{
"ip":"192.168.1.5",
"port":8081
},
{
"ip":"192.168.1.6",
"port":8081
}
]
Spring Cloud收到以后放进本地缓存。
以后调用:
UserService
实际上就是:
192.168.1.5
或者
192.168.1.6
由负载均衡器决定。
五、如何知道服务上下线?
如果每次调用都请求Nacos:
Order
↓
Nacos
↓
User
效率太低。
所以:
Nacos采用"服务订阅(Subscribe)+ 推送(Push)"机制。
流程:
Order启动
↓
订阅user-service
↓
Nacos记录订阅关系
如果:
User3启动
注册:
registerInstance()
Nacos立即知道:
user-service
新增:
192.168.1.8
于是:
Push
↓
Order
↓
更新本地缓存
所以:
Order根本不用重新查询。
六、健康检查(Heartbeat)
如果:
User-Service
突然宕机。
Nacos怎么知道?
答案:
心跳机制。
客户端每隔:
默认5秒
发送:
Beat
POST
/v1/ns/instance/beat
内容:
service=user-service
ip=192.168.1.5
port=8081
Nacos更新时间:
lastBeat
2025-xx-xx
如果:
15秒
没有收到。
实例变成:
healthy=false
如果:
30秒
没有收到。
删除实例:
remove instance
于是:
Order收到Push
↓
删除该节点
七、服务发现为什么这么快?
因为:
消费者不会每次请求Nacos。
而是:
第一次:
Nacos
↓
获取全部实例
↓
本地缓存
之后:
调用
↓
本地缓存
实例变化:
Nacos Push
↓
更新缓存
所以:
真正访问Nacos的次数非常少。
八、负载均衡是如何工作的?
假设:
User
192.168.1.1
192.168.1.2
192.168.1.3
Spring Cloud LoadBalancer 获取实例列表:
List<ServiceInstance>
默认:
Round Robin
例如:
1
2
3
1
2
3
也可以自定义:
- 随机(Random)
- 权重(Weight)
- 一致性哈希(Consistent Hash)
- 最少连接(Least Connections,需要自行实现)
Nacos 本身也支持实例权重(Weight),客户端可以根据权重进行流量分配。
九、Nacos 注册与发现整体时序图
+----------------------+
| Nacos Server |
+----------------------+
▲ ▲
注册实例 │ │ 查询/订阅服务
(registerInstance) │ │ (getAllInstances/subscribe)
│ │
+--------------+ +--------------+
| |
+-------------+ +--------------+
| UserService | | OrderService |
+-------------+ +--------------+
│ │
│ 心跳(默认5秒) │
└──────────────► 更新实例状态 │
│ │
实例上下线变更 ◄─┴────── 推送(Push)─────┘
十、面试回答(2~3 分钟版本)
Nacos 的服务注册采用的是客户端主动注册模式。服务启动时,Spring Cloud Alibaba 会通过
NacosAutoServiceRegistration收集当前实例的服务名、IP、端口、权重等信息,然后调用 Nacos 的registerInstance接口,将实例注册到 Nacos Server 中。Nacos 内部会按照 Namespace → Group → Service → Cluster → Instance 的层级维护服务实例列表。服务发现时,服务消费者不会直接访问提供者,而是通过
NamingService.getAllInstances()从 Nacos 获取目标服务的实例列表,并缓存到本地。之后实际调用由 Spring Cloud LoadBalancer 根据负载均衡策略(如轮询)从缓存中选择一个实例进行访问。为了减少频繁查询注册中心的开销,Nacos 使用 订阅 + 推送(Push) 的机制。当服务实例上下线时,Nacos 会主动通知已订阅的消费者更新本地缓存,而不是每次请求都查询注册中心。
同时,服务实例会定期发送心跳(默认约每 5 秒一次)到 Nacos。若连续多个心跳周期未收到心跳,Nacos 会先将实例标记为不健康,持续超时后再将实例剔除,并通知订阅者更新服务列表。这种机制保证了服务注册、发现和故障摘除都能够较快完成。
浙公网安备 33010602011771号