记得去年在外省给某事业单位给科技处的领导作关于国产化微服务项目的汇报,该处长要我详细讲解一下Nacos的来龙去脉。我问他为什么要单独了解这块,他说现在国产化已经是趋势了,他其实也想深度的了解一下,这款产品是如何演化而来,希望通过了解该产品的来龙去脉深度思索一下,他所辖范围之内系统国产化的一些思路。记得当时我也是做足了工作,然后选择一个时间给他单独汇报,会后领导反响还不错,领导总结道:Nacos适应了国内企业数字化转型的需求,契合了国内企业对于构建稳定、高效微服务体系的诉求。今天我将当时的课件整理一下,在这里留下文字痕迹。也希望用这种方式和大家一起学习探讨。
一、Nacos 的来龙去脉
1、 起源:解决大规模微服务治理痛点
2015 年,阿里巴巴内部微服务规模已达数万实例,传统服务发现组件(如 ZooKeeper)在弹性伸缩、配置管理等方面逐渐暴露出不足:
- 服务雪崩:某服务故障导致调用链崩溃
- 配置管理混乱:不同环境配置分散维护困难
- 服务发现延迟高:大规模集群下服务变更传播缓慢
因此,阿里中间件团队开发了 Nacos(Naming and Configuration Service),作为内部微服务基础设施的核心组件。
2、从内部工具到 Apache 顶级项目
2018 年,Nacos 正式开源并加入 Spring Cloud 生态,迅速成为国内微服务领域的主流技术:
- 2018.06:Nacos 0.1.0 版本发布
- 2019.07:加入 Apache 孵化器
- 2020.06:成为 Apache 顶级项目
- 2023.07:发布 2.0 版本,引入 gRPC 通信协议,性能提升 10 倍
3、行业地位:国内微服务注册中心首选
根据 InfoQ 2023 年微服务技术调研:
- 65% 的国内企业在使用 Nacos 作为服务注册中心
- 90% 的头部互联网公司已在生产环境部署 Nacos
- 支持字节跳动、腾讯、美团等超大规模微服务集群
二、Nacos 工作原理:服务治理的 "交通指挥系统"
1、核心组件与架构
为了便于理解,我们可以Nacos 的整体架构可以类比为一个城市的交通管理系统:代码部分我以python语言进行注解
+----------------+ +----------------+ +----------------+
| 服务提供者 | | Nacos服务器集群 | | 服务消费者 |
| (如餐厅) | | (交通指挥中心) | | (如食客) |
+----------------+ +----------------+ +----------------+
| ^ ^
| (注册服务: 我这里有川菜) | |
v | |
+----------------+ +----------------+ +----------------+
| 服务注册中心 |<--->| 配置中心 |<--->| 服务发现客户端 |
| (餐厅注册表) | | (交通规则库) | | (食客导航App) |
+----------------+ +----------------+ +----------------+
| | |
| (服务变更通知) | (配置变更推送) |
v v v
+----------------+ +----------------+ +----------------+
| 健康检查模块 | | 事件总线 | | 本地缓存 |
| (巡查员) | | (广播系统) | | (本地餐厅列表) |
+----------------+ +----------------+ +----------------+
2、 服务注册:餐厅如何入驻 "美食地图"
当一个服务提供者启动时,它会向 Nacos 服务器发送注册请求,就像一家新餐厅需要在美食地图上登记信息:
- 注册信息:包括服务名称(如 "订单服务")、IP 地址、端口、权重、元数据(如版本号、所属区域)等
- 临时 / 持久化实例:
- 临时实例:通过心跳机制保持存活(默认 5 秒一次心跳)
- 持久化实例:注册后长期存在,需显式注销
# Python代码示例:服务注册
from nacos import NacosClient
nc = NacosClient("127.0.0.1:8848")
nc.add_naming_instance(
service_name="order-service",
ip="192.168.1.100",
port=8080,
weight=1.0,
metadata={"version": "2.0.0"}
)
3、服务存储:Nacos 如何管理服务清单
Nacos 服务器将服务信息存储在内存和磁盘中:
- 内存数据结构:使用 ConcurrentHashMap 保存服务注册表,保证高并发读写性能
- 磁盘持久化:定期将服务信息持久化到文件,防止重启数据丢失
- 集群数据同步:
- AP 模式:异步复制,性能优先
- CP 模式:使用 Raft 协议保证强一致性
4、服务订阅:食客如何获取最新餐厅列表
服务消费者订阅服务时,Nacos 提供两种模式:
1) 推模式(长轮询)
类似食客在餐厅门口等待叫号:
- 消费者向 Nacos 发送订阅请求,设置 30 秒超时
- Nacos 保持连接,如果服务未变化则等待超时
- 若有服务变更,立即响应并返回新的服务列表
- 消费者收到响应后,立即发起下一次长轮询
2)拉模式
类似食客定期查看手机上的美食地图更新:
- 消费者定时(默认 30 秒)向 Nacos 请求服务列表
- 比较本地缓存的 MD5 值,判断是否有更新
5、 健康检查:如何保证推荐的餐厅是营业的
Nacos 提供多种健康检查方式:
- 客户端心跳:服务提供者定期发送心跳包
- 服务端主动检查:Nacos 服务器主动探测服务端口
- 第三方检查:集成外部健康检查系统
当发现服务不健康时,Nacos 会:
- 将实例标记为不健康状态
- 从服务列表中暂时剔除
- 发送服务变更通知给订阅者
6、配置管理:如何动态调整餐厅营业时间
Nacos 的配置管理就像餐厅修改营业时间并通知老顾客:
- 配置发布:管理员在控制台或通过 API 发布配置
- 配置存储:配置以 KV 形式存储,支持版本管理
- 配置订阅:客户端订阅配置,接收变更通知
- 配置热更新:应用无需重启即可使用新配置
# Python代码示例:配置订阅
def config_changed_callback(args):
print(f"配置变更: {args}")
nc.add_config_watcher(
data_id="order-service.yaml",
group="DEFAULT_GROUP",
callback=config_changed_callback
)
7、服务治理:如何应对餐厅爆满的情况
当订单服务出现高并发时,Nacos 可以:
- 限流:限制每秒处理的请求数量
- 熔断:暂时切断对故障服务的调用
- 降级:返回预设的默认结果
- 负载均衡:根据实例性能动态调整流量分配
这些治理规则可以通过控制台动态配置,无需修改代码。
8、CP 与 AP 模式切换:如何平衡一致性与可用性
Nacos 支持两种一致性协议:
模式 | 适用场景 | 实现方式 | 特点 |
---|---|---|---|
AP | 电商、社交等对可用性要求高的场景 | 异步复制 | 服务注册更快,但可能读到旧数据 |
CP | 金融、支付等对一致性要求高的场景 | Raft 协议 | 数据强一致,但写入性能较低 |
可以通过一个配置文件轻松切换模式:
# 在conf/application.properties中配置
spring.profiles.active=ap
# 或
spring.profiles.active=cp
光说不练可不行,下面以python实操来实现Nacos服务的方式
三、Python 实现 Nacos 服务订阅
1、使用 nacos-sdk-python 库
首先安装 SDK:
pip install nacos-sdk-python
我们来实现一个完整的订阅示例:
from nacos import NacosClient
# 初始化客户端
nc = NacosClient(
server_addresses="127.0.0.1:8848", # Nacos服务器地址
namespace="public", # 命名空间ID
username="nacos", # 用户名
password="nacos" # 密码
)
# 定义服务变更回调函数
def service_changed_callback(services):
print(f"服务列表变更: {services}")
# 这里可以实现服务列表变更后的处理逻辑
# 例如更新本地缓存、通知负载均衡器等
# 订阅服务
nc.subscribe(
service_name="user-service", # 服务名
group_name="DEFAULT_GROUP", # 组名
cluster_name="DEFAULT", # 集群名
callback=service_changed_callback # 回调函数
)
# 获取当前服务列表
instances = nc.list_naming_instance(
service_name="user-service",
group_name="DEFAULT_GROUP",
cluster_name="DEFAULT"
)
print(f"当前服务实例: {instances}")
# 保持主线程运行,使订阅持续生效
try:
import time
while True:
time.sleep(1)
except KeyboardInterrupt:
print("程序退出")
2、 核心 API 解析
方法 | 功能 | 参数说明 |
---|---|---|
NacosClient() | 初始化客户端 | server_addresses : Nacos 服务器地址namespace : 命名空间username/password : 认证信息 |
subscribe() | 订阅服务 | service_name : 服务名group_name : 组名callback : 服务变更回调函数 |
list_naming_instance() | 获取服务实例列表 | service_name : 服务名group_name : 组名clusters : 集群名列表 |
unsubscribe() | 取消订阅 | service_name : 服务名group_name : 组名 |
3、实现原理剖析
当调用subscribe()
方法时,SDK 内部执行以下操作:
- 初始化一个长轮询任务,定时向 Nacos 服务器发送请求
- 在本地维护一个服务实例缓存
- 服务器返回服务列表及 MD5 值
- SDK 比较 MD5 值,判断服务是否发生变更
- 如有变更,调用用户提供的回调函数
再讲一下服务订阅底层的实现机制,总结一下主要在三个方面
四、Nacos 服务订阅底层机制
1、 长轮询机制
Nacos 的推模式实际上是长轮询机制:
- 客户端向服务器发送请求,设置较长的超时时间(默认 30 秒)
- 服务器保持连接,如果服务未变更则等待超时
- 若服务发生变更,服务器立即响应并返回新的服务列表
- 客户端收到响应后,立即发起下一次长轮询请求
这种机制相比传统轮询有以下优势:
- 减少无效请求,降低服务器压力
- 实时性好,服务变更能在秒级内通知到客户端
- 相比真正的推送模式,实现更简单,兼容性更好
2、 服务变更事件处理流程
- 服务提供者注册 / 注销实例时,Nacos 服务器触发服务变更事件
- 事件被发送到事件总线 (EventBus)
- 订阅管理器 (SubscriberManager) 监听事件总线
- 当有匹配的订阅时,构造响应数据
- 通过长轮询连接返回变更数据给客户端
3、数据一致性保障
Nacos 在 AP 模式下,服务注册与订阅采用最终一致性:
- 服务注册信息会异步复制到集群其他节点
- 订阅者可能在短时间内看到不一致的服务列表
- 最终所有节点的数据会达成一致
在 CP 模式下,服务注册与订阅采用强一致性:
- 使用 Raft 协议保证数据在多数节点持久化后才返回成功
- 服务订阅能获取到最新的服务状态
看看实践中代码如何实现吧
五、服务订阅最佳实践
1、健康检查机制
建议配置服务的健康检查:
# 注册服务时配置健康检查
nc.add_naming_instance(
service_name="order-service",
ip="192.168.1.102",
port=8080,
weight=1.0,
cluster_name="DEFAULT",
ephemeral=True, # 临时实例,需要定时发送心跳
metadata={"version": "2.0.0"},
# 配置健康检查
healthy_check_interval=5, # 健康检查间隔(秒)
healthy_check_timeout=10, # 健康检查超时时间(秒)
max_healthy_check_tries=3 # 最大健康检查失败次数
)
5.2 本地缓存策略
为提高性能,建议实现多级缓存:
import threading
import time
from cachetools import TTLCache
# 本地缓存,保存最近300秒的服务信息,最多100个条目
service_cache = TTLCache(maxsize=100, ttl=300)
cache_lock = threading.Lock()
def get_service_instances(service_name):
# 优先从本地缓存获取
with cache_lock:
if service_name in service_cache:
return service_cache[service_name]
# 缓存未命中,从Nacos获取
instances = nc.list_naming_instance(service_name)
# 更新缓存
with cache_lock:
service_cache[service_name] = instances
return instances
5.3 异常处理与重试
import requests
from tenacity import retry, stop_after_attempt, wait_fixed
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
def safe_subscribe(service_name):
try:
nc.subscribe(
service_name=service_name,
callback=service_changed_callback
)
except requests.exceptions.RequestException as e:
print(f"订阅失败: {e}, 正在重试...")
raise # 触发重试
六、性能优化与常见问题
1、性能优化建议
- 控制订阅数量,避免订阅过多不必要的服务
- 合理设置长轮询超时时间,生产环境建议 30 秒
- 对服务列表进行本地缓存,减少 API 调用
- 使用连接池,复用 HTTP 连接
- 对于核心服务,考虑使用 CP 模式保证数据一致性
2、常见问题及解决方案
问题 | 原因 | 解决方案 |
---|---|---|
订阅不生效 | 命名空间、组名不匹配 | 检查命名空间 ID、组名是否正确 |
服务变更不通知 | 长轮询连接断开 | 实现断线重连机制,添加心跳检测 |
获取不到服务实例 | 服务未注册或已下线 | 检查服务提供者是否正常运行,查看 Nacos 控制台 |
订阅延迟高 | 网络问题或 Nacos 负载过高 | 检查网络状况,增加 Nacos 集群节点 |
七、生产环境部署建议
1、高可用部署
建议采用至少 3 节点的集群部署模式:
+----------------+ +----------------+ +----------------+
| Nacos Node 1 | | Nacos Node 2 | | Nacos Node 3 |
| 192.168.1.10 |<--->| 192.168.1.11 |<--->| 192.168.1.12 |
+----------------+ +----------------+ +----------------+
^ ^ ^
| | |
+----------------+ +----------------+ +----------------+
| 服务提供者 | | 服务消费者 | | 配置中心客户端 |
| 应用集群 | | 应用集群 | | 应用集群 |
+----------------+ +----------------+ +----------------+
7.2 监控与告警
建议监控以下指标:
- Nacos 服务器 CPU、内存使用率
- 服务注册与发现请求 QPS
- 长轮询连接数
- 服务健康检查成功率
- 服务变更事件处理延迟
7.3 安全加固
- 启用 Nacos 认证机制,设置用户名密码
- 配置 IP 白名单,限制访问来源
- 使用 HTTPS 协议通信
- 定期更新 Nacos 版本,修复安全漏洞
最后小结
Nacos 确实是具有中国 “血统” 的开源产品,由阿里巴巴开发并开源 。它脱胎于阿里内部从 2008 年就开始发展的 ConfigServer,在 2018 年 7 月发布首个开源版本 V0.1.0,历经版本迭代,不断完善功能并提升性能。逐步发展成为一个成熟且被广泛应用的开源项目,展示了中国企业在分布式系统、微服务架构等关键技术领域具备强大的自主研发与创新能力。它并非对国外同类产品的模仿,而是基于自身大规模业务场景的实践经验,针对性地解决微服务治理过程中的痛点问题,如服务雪崩、配置管理混乱、服务发现延迟高等,创造出了符合本土乃至全球开发者需求的服务发现与配置管理平台。
在Nacos 在发展过程中,也是不断适配国产的操作系统、数据库等基础软件。所以目前Nacos 的服务订阅机制是微服务架构中不可或缺的一环,掌握 Nacos 服务订阅技术,能够帮助开发者构建更加弹性、高效的微服务系统,实现服务间的智能调用与治理。我也是通过 Python SDK 从基础概念入手,深入剖析了 Nacos 服务订阅的核心机制、Python 实现方式以及生产环境的最佳实践。总而言之,Nacos 作为优秀的开源解决方案,值得开发者深入学习与应用。