Skip to content

Nacos 集成

Nacos 是阿里巴巴开源的服务发现、配置管理和服务管理平台,在微服务架构中承担注册中心配置中心的角色。 官方文档:https://nacos.io/ Python SDK:https://github.com/nacos-group/nacos-sdk-python

Nacos 核心能力

能力说明FastAPI 中的用途
服务注册与发现服务实例自动注册,消费者通过服务名发现实例Python 服务被其他微服务发现
配置管理集中管理配置,支持动态刷新运行时热更新配置
健康检查定期检测服务实例健康状态心跳保活
命名空间多环境隔离(dev/test/prod)环境隔离

1. 环境准备

1.1 安装 Nacos Server

bash
# Docker 快速启动(单机模式)
docker pull nacos/nacos-server
docker run -d \
    --name nacos-server \
    -p 8848:8848 \
    -p 9848:9848 \
    -e MODE=standalone \
    -e NACOS_AUTH_ENABLE=true \
    nacos/nacos-server

# 访问控制台:http://localhost:8848/nacos
# 默认账号:nacos / nacos

1.2 安装 Python SDK

bash
pip install nacos-sdk-python

SDK 版本说明

  • v1nacos-sdk-python):支持 Python 2.7/3.6/3.7,兼容 Nacos 1.x 和 2.x(HTTP 协议)
  • v2:支持 Python 3.7+,原生 Nacos 2.x gRPC 协议
  • 本文使用 v1 API(当前稳定版),导入方式为 import nacos

1.3 Nacos 核心概念

概念说明示例
Namespace命名空间,环境隔离dev / test / prod
Group配置分组DEFAULT_GROUP / FASTAPI_GROUP
Data ID配置标识user-service.yaml
Service Name服务名称user-service
Instance服务实例(IP:Port)192.168.1.10:8000

2. 服务注册与发现

2.1 完整的服务注册代码

python
# nacos_config.py
"""Nacos 连接配置"""

NACOS_SERVER = "127.0.0.1:8848"
NAMESPACE = "public"                       # 命名空间 ID
GROUP = "DEFAULT_GROUP"                    # 分组
USERNAME = "nacos"                         # 认证用户名
PASSWORD = "nacos"                         # 认证密码

# 当前服务信息
SERVICE_NAME = "fastapi-user-service"
SERVICE_IP = "127.0.0.1"
SERVICE_PORT = 8000
python
# nacos_client.py
"""Nacos 客户端封装:服务注册与发现"""

import nacos
import socket
from nacos_config import (
    NACOS_SERVER, NAMESPACE, GROUP,
    USERNAME, PASSWORD,
    SERVICE_NAME, SERVICE_IP, SERVICE_PORT,
)

def get_local_ip() -> str:
    """获取本机 IP"""
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        ip = s.getsockname()[0]
        s.close()
        return ip
    except Exception:
        return "127.0.0.1"


class NacosServiceRegistry:
    """Nacos 服务注册与发现"""

    def __init__(self):
        self.client = nacos.NacosClient(
            NACOS_SERVER,
            namespace=NAMESPACE,
            username=USERNAME,
            password=PASSWORD,
        )
        self.service_ip = SERVICE_IP or get_local_ip()
        self.service_port = SERVICE_PORT

    # ---- 服务注册 ----
    def register(self):
        """注册当前服务到 Nacos"""
        self.client.add_naming_instance(
            SERVICE_NAME,
            self.service_ip,
            self.service_port,
            group_name=GROUP,
            weight=1.0,
            enabled=True,
            healthy=True,
            metadata={
                "version": "1.0.0",
                "env": "production",
                "framework": "fastapi",
            },
        )
        print(f"[Nacos] 服务注册成功: {SERVICE_NAME}@{self.service_ip}:{self.service_port}")

    # ---- 服务注销 ----
    def deregister(self):
        """从 Nacos 注销当前服务"""
        self.client.remove_naming_instance(
            SERVICE_NAME,
            self.service_ip,
            self.service_port,
            group_name=GROUP,
        )
        print(f"[Nacos] 服务已注销: {SERVICE_NAME}")

    # ---- 服务发现 ----
    def get_instances(self, service_name: str = SERVICE_NAME) -> list[dict]:
        """获取服务的所有实例"""
        result = self.client.list_naming_instance(
            service_name,
            group_name=GROUP,
            healthy_only=True,
        )
        return result.get("hosts", [])

    # ---- 选择一个实例(简单轮询) ----
    _instance_index = 0

    def choose_instance(self, service_name: str) -> dict | None:
        """轮询选择一个健康实例"""
        instances = self.get_instances(service_name)
        if not instances:
            return None
        instance = instances[self._instance_index % len(instances)]
        NacosServiceRegistry._instance_index += 1
        return instance

    # ---- 心跳保活 ----
    def send_heartbeat(self):
        """发送心跳(Nacos 默认自动检测,可手动触发)"""
        self.client.send_heartbeat(
            SERVICE_NAME,
            self.service_ip,
            self.service_port,
            group_name=GROUP,
            metadata={
                "version": "1.0.0",
                "env": "production",
            },
        )

2.2 FastAPI 集成服务注册

python
# main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from nacos_client import NacosServiceRegistry

# 全局 Nacos 注册器
registry = NacosServiceRegistry()
scheduler = AsyncIOScheduler()


@asynccontextmanager
async def lifespan(app: FastAPI):
    # ---- 启动:注册服务 + 启动心跳 ----
    registry.register()

    # 每 10 秒发送一次心跳(可选,Nacos 默认自动检测)
    scheduler.add_job(registry.send_heartbeat, "interval", seconds=10)
    scheduler.start()

    yield

    # ---- 关闭:注销服务 ----    scheduler.shutdown()
    registry.deregister()


app = FastAPI(title="User Service", lifespan=lifespan)


@app.get("/users/")
async def list_users():
    return {"users": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]}


@app.get("/health")
async def health():
    return {"status": "UP"}


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

安装 APScheduler

心跳定时任务需要:pip install apscheduler

2.3 服务发现 -- 调用其他微服务

python
# service_caller.py
"""通过 Nacos 服务发现调用其他微服务"""

import httpx
from nacos_client import NacosServiceRegistry

registry = NacosServiceRegistry()


async def call_service(service_name: str, path: str, method: str = "GET", **kwargs):
    """通过服务名调用微服务"""
    # 1. 从 Nacos 获取健康实例
    instance = registry.choose_instance(service_name)
    if not instance:
        raise RuntimeError(f"服务 {service_name} 无可用实例")

    # 2. 构造请求 URL
    host = instance["ip"]
    port = instance["port"]
    url = f"http://{host}:{port}{path}"

    # 3. 发送请求
    async with httpx.AsyncClient() as client:
        if method.upper() == "GET":
            response = await client.get(url, **kwargs)
        elif method.upper() == "POST":
            response = await client.post(url, **kwargs)
        else:
            response = await client.request(method, url, **kwargs)

    return response.json()


# 在 FastAPI 路由中使用
@app.get("/orders/{order_id}")
async def get_order(order_id: int):
    # 通过服务发现调用 user-service
    user = await call_service("fastapi-user-service", f"/users/{order_id}")
    return {"order_id": order_id, "user": user}

3. 配置中心

3.1 读取配置

python
# nacos_config_manager.py
"""Nacos 配置管理"""

import nacos
import json
from nacos_config import NACOS_SERVER, NAMESPACE, GROUP, USERNAME, PASSWORD

client = nacos.NacosClient(
    NACOS_SERVER,
    namespace=NAMESPACE,
    username=USERNAME,
    password=PASSWORD,
)


def get_config(data_id: str, group: str = GROUP) -> str:
    """获取配置(字符串)"""
    return client.get_config(data_id, group)


def get_config_json(data_id: str, group: str = GROUP) -> dict:
    """获取配置(解析为 JSON)"""
    raw = client.get_config(data_id, group)
    return json.loads(raw) if raw else {}


def publish_config(data_id: str, content: str, group: str = GROUP):
    """发布/更新配置"""
    client.publish_config(data_id, group, content)


def remove_config(data_id: str, group: str = GROUP):
    """删除配置"""
    client.remove_config(data_id, group)

3.2 配置监听(动态刷新)

python
# config_watcher.py
"""Nacos 配置监听,实现动态刷新"""

import json
import nacos
from nacos_config import NACOS_SERVER, NAMESPACE, GROUP, USERNAME, PASSWORD

client = nacos.NacosClient(
    NACOS_SERVER,
    namespace=NAMESPACE,
    username=USERNAME,
    password=PASSWORD,
)

# 全局配置缓存
app_config: dict = {}


def config_callback(args):
    """配置变更回调"""
    global app_config
    raw = args.get("raw", "")
    if raw:
        app_config = json.loads(raw)
        print(f"[Nacos] 配置已更新: {app_config}")


def start_config_watcher(data_id: str, group: str = GROUP):
    """启动配置监听"""
    global app_config

    # 1. 首次加载配置
    raw = client.get_config(data_id, group)
    if raw:
        app_config = json.loads(raw)
        print(f"[Nacos] 配置已加载: {app_config}")

    # 2. 添加监听(后台线程轮询变更)
    client.add_config_watcher(data_id, group, config_callback)
    print(f"[Nacos] 配置监听已启动: {data_id}")

3.3 FastAPI 集成配置中心

python
# main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from config_watcher import app_config, start_config_watcher
from nacos_client import NacosServiceRegistry

DATA_ID = "fastapi-user-service.json"
registry = NacosServiceRegistry()


@asynccontextmanager
async def lifespan(app: FastAPI):
    # 启动配置监听
    start_config_watcher(DATA_ID)

    # 注册服务
    registry.register()

    yield

    # 注销服务
    registry.deregister()


app = FastAPI(lifespan=lifespan)


@app.get("/config")
async def get_current_config():
    """查看当前配置(实时读取缓存,配置变更后自动更新)"""
    return app_config


@app.get("/users/")
async def list_users():
    # 使用配置中的分页参数
    page_size = app_config.get("page_size", 10)
    return {"page_size": page_size, "users": []}

3.4 Nacos 配置格式

在 Nacos 控制台创建配置,Data ID 为 fastapi-user-service.json,格式选择 JSON:

json
{
    "page_size": 20,
    "max_retries": 3,
    "enable_cache": true,
    "cache_ttl": 300,
    "log_level": "INFO",
    "feature_flags": {
        "new_dashboard": true,
        "beta_feature": false
    }
}

支持的配置格式:JSONYAMLPropertiesTEXTXMLHTML


4. 多命名空间(环境隔离)

python
# nacos_multi_env.py
"""多环境 Nacos 配置"""

import nacos

# 不同环境使用不同命名空间
NAMESPACES = {
    "dev":  "dev-namespace-id",      # 开发环境
    "test": "test-namespace-id",     # 测试环境
    "prod": "prod-namespace-id",     # 生产环境
}

def get_client(env: str = "dev") -> nacos.NacosClient:
    """获取指定环境的 Nacos 客户端"""
    namespace_id = NAMESPACES.get(env)
    if not namespace_id:
        raise ValueError(f"未知环境: {env}")

    return nacos.NacosClient(
        "127.0.0.1:8848",
        namespace=namespace_id,
        username="nacos",
        password="nacos",
    )

# 使用示例
dev_client = get_client("dev")
config = dev_client.get_config("user-service.json", "DEFAULT_GROUP")

5. 完整项目结构

fastapi-nacos-demo/
├── main.py                  # FastAPI 应用入口
├── nacos_config.py          # Nacos 连接配置
├── nacos_client.py          # 服务注册与发现
├── nacos_config_manager.py  # 配置读写
├── config_watcher.py        # 配置监听与动态刷新
├── service_caller.py        # 微服务间调用
└── requirements.txt

requirements.txt

fastapi==0.115.0
uvicorn[standard]==0.30.0
nacos-sdk-python==0.1.14
httpx==0.27.0
apscheduler==3.10.4

6. 与 Spring Cloud 生态集成

FastAPI + Nacos 可以与 Java 微服务生态无缝对接:

┌─────────────────┐     ┌──────────────────┐
│  Spring Cloud   │     │  FastAPI          │
│  Gateway        │────→│  User Service     │
│  (Java)         │     │  (Python)         │
└────────┬────────┘     └────────┬─────────┘
         │                       │
         ▼                       ▼
    ┌──────────────────────────────┐
    │         Nacos Server         │
    │  (注册中心 + 配置中心)        │
    └──────────────────────────────┘

关键要点:

  • Python 服务注册到 Nacos 后,Java 服务可以通过服务名发现并调用 Python 服务
  • Spring Cloud Gateway 可以将 /api/users/** 路由到 Python FastAPI 服务
  • 配置格式保持一致(JSON/YAML),便于统一管理

7. 常见错误排查

错误 1:连接 Nacos 超时

原因:Nacos Server 未启动或地址配置错误
解决:检查 Nacos 是否运行,确认 IP 和端口
验证:curl http://localhost:8848/nacos/v1/console/health/readiness

错误 2:服务注册后不可见

原因:命名空间 ID 不匹配(不是名称,而是 UUID)
解决:在 Nacos 控制台复制命名空间 ID(如 8b6f08b7-xxxx)
注意:public 命名空间的 ID 为空字符串 ""

错误 3:配置读取为空

原因:Data ID 或 Group 不匹配
解决:确认 Nacos 控制台中的 Data ID、Group 与代码一致
注意:Data ID 需包含扩展名(如 .json、.yaml)

错误 4:No module named 'nacos'

原因:未安装 nacos-sdk-python
解决:pip install nacos-sdk-python

8. 对比:Nacos vs 其他配置中心

特性NacosConsuletcdApollo
服务注册支持支持支持不支持
配置管理支持不支持不支持支持
配置热更新支持--支持
多环境隔离命名空间不支持不支持支持
Python SDK官方支持
生态整合Spring Cloud 原生通用Kubernetes通用
社区活跃度

相关笔记

  • 00-FastAPI学习总览 -- 学习路线索引
  • 06-SQLAlchemy集成 -- 数据库配置与 Nacos 配置中心配合
  • 07-Redis集成 -- Redis 配置可从 Nacos 动态获取
  • 08-部署 -- 生产环境部署与 Nacos 集群

基于 MIT 许可发布