Nacos的安装与使用

Nacos 介绍

Nacos (Dynamic Naming and Configuration Service)是阿里巴巴开源的一个动态服务发现、配置管理和服务管理平台。

主要作用

  1. 服务注册与发现:微服务可以注册到 Nacos,其他服务通过 Nacos 发现并调用它们
  2. 配置管理:集中管理应用配置,支持动态配置更新,无需重启应用
  3. 服务健康监测:实时监控服务健康状态,自动剔除不健康实例
  4. 动态 DNS 服务:支持基于 DNS 的服务发现

安装/启动

docker run -d \  
  --name nacos \  
  --restart=always \  
  --platform linux/arm64 \  
  -e MODE=standalone \  
  -e NACOS_AUTH_ENABLE=true \  
  -e NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789 \  
  -e NACOS_AUTH_IDENTITY_KEY=nacos \  
  -e NACOS_AUTH_IDENTITY_VALUE=nacos \  
  -e JVM_XMS=512m \  
  -e JVM_XMX=512m \  
  -p 8080:8080 \  
  -p 8848:8848 \  
  -p 9848:9848 \  
  -p 9849:9849 \  
  nacos/nacos-server:latest

等待启动

echo "等待 Nacos 启动..." sleep 25

验证端口映射

docker port nacos

检查启动状态

docker logs nacos | grep "started successfully"

访问控制台

访问: http://localhost:8080

  • 用户名: nacos
  • 密码: nacos

(第一次访问要设置密码)

番外: 排查问题 常用命令

# 查看日志  
docker logs -f nacos  

# 停止 Nacos  
docker stop nacos  

# 启动 Nacos  
docker start nacos  

# 重启 Nacos  
docker restart nacos  

# 删除容器  
docker rm -f nacos

使用

登录后,会看到 Nacos 控制台 左侧菜单有以下几个主要模块:

配置管理 (最常用)

用于集中管理微服务的配置文件

服务管理

用于服务注册与发现

命名空间

用于环境隔离(开发、测试、生产)

集群管理

查看 Nacos 集群状态


快速上手:配置管理

创建配置

  1. 点击左侧菜单 配置管理 → 配置列表
  2. 点击右上角 "+" 按钮创建配置
  3. 填写以下信息:
Data ID: application.yml  
Group: DEFAULT_GROUP  
配置格式: YAML  
配置内容:  
server:  
  port: 8081  

spring:  
  application:  
    name: demo-service  

message: "Hello from Nacos!"
  1. 点击 发布

查看配置

  • 在配置列表中可以看到刚创建的配置
  • 点击配置可以查看详情、编辑、查看历史版本
  • 支持配置回滚

服务注册与发现示例

查看服务列表

  1. 点击 服务管理 → 服务列表
  2. 目前是空的,需要应用注册后才能看到

使用命名空间隔离环境

创建命名空间

  1. 点击 命名空间
  2. 点击 新建命名空间
  3. 填写信息:
命名空间名: dev  
命名空间ID: (自动生成)  
描述: 开发环境
  1. 同样创建 test 和 prod 命名空间

在 Spring Boot 项目中使用 Nacos

1. 添加依赖 (pom.xml)

<dependencies>  
    <!-- Nacos 配置管理 -->  
    <dependency>  
        <groupId>com.alibaba.cloud</groupId>  
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>  
        <version>2022.0.0.0</version>  
    </dependency>  

    <!-- Nacos 服务发现 -->  
    <dependency>  
        <groupId>com.alibaba.cloud</groupId>  
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>  
        <version>2022.0.0.0</version>  
    </dependency>  
</dependencies>

2. 配置文件 (bootstrap.yml)

spring:  
  application:  
    name: my-service  
  cloud:  
    nacos:  
      # 配置中心  
      config:  
        server-addr: localhost:8848  
        file-extension: yaml  
        namespace: dev  # 使用 dev 命名空间  
        username: nacos  
        password: nacos  
      # 服务发现  
      discovery:  
        server-addr: localhost:8848  
        namespace: dev  
        username: nacos  
        password: nacos

3. 启动类添加注解

@SpringBootApplication  
@EnableDiscoveryClient  // 启用服务发现  
public class MyServiceApplication {  
    public static void main(String[] args) {  
        SpringApplication.run(MyServiceApplication.class, args);  
    }  
}

4. 读取配置

@RestController  
@RefreshScope  // 支持配置动态刷新  
public class ConfigController {  

    @Value("${message:default}")  
    private String message;  

    @GetMapping("/config")  
    public String getConfig() {  
        return message;  
    }  
}

实用操作技巧

配置动态刷新

  1. 修改 Nacos 中的配置
  2. 点击 发布
  3. 应用会自动获取最新配置(无需重启)

配置搜索

  • 支持按 Data ID、Group 搜索
  • 支持模糊搜索

配置历史

  • 点击配置详情 → 历史版本
  • 可以对比版本差异
  • 支持回滚到历史版本

配置监听查询

  • 查看哪些客户端正在监听某个配置
  • 在配置详情页可以看到

常见使用场景

场景 1: 多环境配置管理

命名空间: dev, test, prod  
Data ID: application.yml  
不同环境配置不同的数据库连接等

场景 2: 微服务配置

Data ID: user-service.yml     (用户服务配置)  
Data ID: order-service.yml    (订单服务配置)  
Data ID: common.yml           (公共配置)

场景 3: 动态开关

# 在 Nacos 中配置  
feature:  
  newFeature: true  # 新功能开关  

# 在代码中使用  
@Value("${feature.newFeature}")  
private boolean newFeatureEnabled;

监控与运维

查看服务健康状态

  • 服务管理 → 服务列表 → 点击服务名
  • 可以看到服务实例的健康状态、IP、端口等

服务下线

  • 点击服务实例的 下线 按钮
  • 该实例将不再接收流量

实践建议

  1. 命名规范: Data ID 建议使用 ${应用名}-${环境}.${后缀} 格式
  2. 分组使用: 可以按业务模块分组,如 USER_GROUPORDER_GROUP
  3. 权限管理: 生产环境建议修改默认密码,配置权限
  4. 备份配置: 定期导出重要配置做备份

在 Golang 中使用 Nacos

阿里巴巴提供了官方的 Go SDK

一、安装 Nacos Go SDK

go get -u github.com/nacos-group/nacos-sdk-go/v2

二、配置管理示例

1. 基础配置读取

package main  

import (  
    "fmt"  
    "github.com/nacos-group/nacos-sdk-go/v2/clients"  
    "github.com/nacos-group/nacos-sdk-go/v2/common/constant"  
    "github.com/nacos-group/nacos-sdk-go/v2/vo"  
)  

func main() {  
    // 创建 ServerConfig  
    sc := []constant.ServerConfig{  
        {  
            IpAddr: "localhost",  
            Port:   8848,  
        },  
    }  

    // 创建 ClientConfig  
    cc := constant.ClientConfig{  
        NamespaceId:         "dev", // 命名空间ID,如果是public则留空  
        TimeoutMs:           5000,  
        NotLoadCacheAtStart: true,  
        LogDir:              "/tmp/nacos/log",  
        CacheDir:            "/tmp/nacos/cache",  
        LogLevel:            "info",  
        Username:            "nacos",  
        Password:            "nacos",  
    }  

    // 创建配置客户端  
    configClient, err := clients.NewConfigClient(  
        vo.NacosClientParam{  
            ClientConfig:  &cc,  
            ServerConfigs: sc,  
        },  
    )  
    if err != nil {  
        panic(err)  
    }  

    // 获取配置  
    content, err := configClient.GetConfig(vo.ConfigParam{  
        DataId: "application.yml",  
        Group:  "DEFAULT_GROUP",  
    })  
    if err != nil {  
        panic(err)  
    }  
    fmt.Println("配置内容:\n", content)  
}

2. 监听配置变化(动态刷新)

package main  

import (  
    "fmt"  
    "time"  
    "github.com/nacos-group/nacos-sdk-go/v2/clients"  
    "github.com/nacos-group/nacos-sdk-go/v2/common/constant"  
    "github.com/nacos-group/nacos-sdk-go/v2/vo"  
)  

func main() {  
    // 创建配置客户端(同上)  
    sc := []constant.ServerConfig{{IpAddr: "localhost", Port: 8848}}  
    cc := constant.ClientConfig{  
        NamespaceId: "",  
        TimeoutMs:   5000,  
        Username:    "nacos",  
        Password:    "nacos",  
    }  

    configClient, err := clients.NewConfigClient(  
        vo.NacosClientParam{  
            ClientConfig:  &cc,  
            ServerConfigs: sc,  
        },  
    )  
    if err != nil {  
        panic(err)  
    }  

    // 监听配置变化  
    err = configClient.ListenConfig(vo.ConfigParam{  
        DataId: "application.yml",  
        Group:  "DEFAULT_GROUP",  
        OnChange: func(namespace, group, dataId, data string) {  
            fmt.Println("配置发生变化:")  
            fmt.Println("Namespace:", namespace)  
            fmt.Println("Group:", group)  
            fmt.Println("DataId:", dataId)  
            fmt.Println("新配置内容:\n", data)  
        },  
    })  
    if err != nil {  
        panic(err)  
    }  

    // 保持程序运行  
    select {}  
}

3. 发布配置

package main  

import (  
    "fmt"  
    "github.com/nacos-group/nacos-sdk-go/v2/clients"  
    "github.com/nacos-group/nacos-sdk-go/v2/common/constant"  
    "github.com/nacos-group/nacos-sdk-go/v2/vo"  
)  

func main() {  
    // 创建配置客户端  
    sc := []constant.ServerConfig{{IpAddr: "localhost", Port: 8848}}  
    cc := constant.ClientConfig{  
        Username: "nacos",  
        Password: "nacos",  
    }  

    configClient, err := clients.NewConfigClient(  
        vo.NacosClientParam{  
            ClientConfig:  &cc,  
            ServerConfigs: sc,  
        },  
    )  
    if err != nil {  
        panic(err)  
    }  

    // 发布配置  
    success, err := configClient.PublishConfig(vo.ConfigParam{  
        DataId:  "test-config.yml",  
        Group:   "DEFAULT_GROUP",  
        Content: "server:\n  port: 8080\n  name: test-service",  
    })  
    if err != nil {  
        panic(err)  
    }  
    fmt.Println("发布配置成功:", success)  
}

三、服务注册与发现

1. 服务注册

package main  

import (  
    "fmt"  
    "time"  
    "github.com/nacos-group/nacos-sdk-go/v2/clients"  
    "github.com/nacos-group/nacos-sdk-go/v2/common/constant"  
    "github.com/nacos-group/nacos-sdk-go/v2/vo"  
)  

func main() {  
    // 创建服务发现客户端  
    sc := []constant.ServerConfig{{IpAddr: "localhost", Port: 8848}}  
    cc := constant.ClientConfig{  
        NamespaceId: "",  
        TimeoutMs:   5000,  
        Username:    "nacos",  
        Password:    "nacos",  
    }  

    namingClient, err := clients.NewNamingClient(  
        vo.NacosClientParam{  
            ClientConfig:  &cc,  
            ServerConfigs: sc,  
        },  
    )  
    if err != nil {  
        panic(err)  
    }  

    // 注册服务实例  
    success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{  
        Ip:          "127.0.0.1",  
        Port:        8080,  
        ServiceName: "user-service",  
        Weight:      10,  
        Enable:      true,  
        Healthy:     true,  
        Ephemeral:   true,  
        Metadata:    map[string]string{"version": "1.0"},  
        ClusterName: "DEFAULT",  
        GroupName:   "DEFAULT_GROUP",  
    })  
    if err != nil {  
        panic(err)  
    }  
    fmt.Println("注册服务成功:", success)  

    // 保持服务运行  
    time.Sleep(time.Hour)  
}

2. 服务发现

package main  

import (  
    "fmt"  
    "github.com/nacos-group/nacos-sdk-go/v2/clients"  
    "github.com/nacos-group/nacos-sdk-go/v2/common/constant"  
    "github.com/nacos-group/nacos-sdk-go/v2/vo"  
)  

func main() {  
    // 创建服务发现客户端  
    sc := []constant.ServerConfig{{IpAddr: "localhost", Port: 8848}}  
    cc := constant.ClientConfig{  
        Username: "nacos",  
        Password: "nacos",  
    }  

    namingClient, err := clients.NewNamingClient(  
        vo.NacosClientParam{  
            ClientConfig:  &cc,  
            ServerConfigs: sc,  
        },  
    )  
    if err != nil {  
        panic(err)  
    }  

    // 获取服务实例列表  
    instances, err := namingClient.SelectInstances(vo.SelectInstancesParam{  
        ServiceName: "user-service",  
        GroupName:   "DEFAULT_GROUP",  
        HealthyOnly: true, // 只返回健康实例  
    })  
    if err != nil {  
        panic(err)  
    }  

    fmt.Println("服务实例列表:")  
    for _, instance := range instances {  
        fmt.Printf("IP: %s, Port: %d, Weight: %.2f\n",  
            instance.Ip, instance.Port, instance.Weight)  
    }  
}

3. 订阅服务变化

package main  

import (  
    "fmt"  
    "github.com/nacos-group/nacos-sdk-go/v2/clients"  
    "github.com/nacos-group/nacos-sdk-go/v2/common/constant"  
    "github.com/nacos-group/nacos-sdk-go/v2/model"  
    "github.com/nacos-group/nacos-sdk-go/v2/vo"  
)  

func main() {  
    // 创建服务发现客户端  
    sc := []constant.ServerConfig{{IpAddr: "localhost", Port: 8848}}  
    cc := constant.ClientConfig{  
        Username: "nacos",  
        Password: "nacos",  
    }  

    namingClient, err := clients.NewNamingClient(  
        vo.NacosClientParam{  
            ClientConfig:  &cc,  
            ServerConfigs: sc,  
        },  
    )  
    if err != nil {  
        panic(err)  
    }  

    // 订阅服务变化  
    err = namingClient.Subscribe(&vo.SubscribeParam{  
        ServiceName: "user-service",  
        GroupName:   "DEFAULT_GROUP",  
        SubscribeCallback: func(services []model.Instance, err error) {  
            fmt.Println("服务列表发生变化:")  
            for _, service := range services {  
                fmt.Printf("  - %s:%d (健康: %v)\n",  
                    service.Ip, service.Port, service.Healthy)  
            }  
        },  
    })  
    if err != nil {  
        panic(err)  
    }  

    // 保持程序运行  
    select {}  
}

四、完整的 Web 服务示例

结合 Gin 框架的完整示例:

package main  

import (  
    "fmt"  
    "github.com/gin-gonic/gin"  
    "github.com/nacos-group/nacos-sdk-go/v2/clients"  
    "github.com/nacos-group/nacos-sdk-go/v2/common/constant"  
    "github.com/nacos-group/nacos-sdk-go/v2/vo"  
    "gopkg.in/yaml.v3"  
    "log"  
)  

// 配置结构体  
type AppConfig struct {  
    Server struct {  
        Port int    `yaml:"port"`  
        Name string `yaml:"name"`  
    } `yaml:"server"`  
    Message string `yaml:"message"`  
}  

var appConfig AppConfig  

func main() {  
    // 1. 创建 Nacos 配置客户端  
    sc := []constant.ServerConfig{{IpAddr: "localhost", Port: 8848}}  
    cc := constant.ClientConfig{  
        NamespaceId: "",  
        TimeoutMs:   5000,  
        Username:    "nacos",  
        Password:    "nacos",  
    }  

    configClient, err := clients.NewConfigClient(  
        vo.NacosClientParam{  
            ClientConfig:  &cc,  
            ServerConfigs: sc,  
        },  
    )  
    if err != nil {  
        log.Fatal(err)  
    }  

    // 2. 获取配置  
    content, err := configClient.GetConfig(vo.ConfigParam{  
        DataId: "application.yml",  
        Group:  "DEFAULT_GROUP",  
    })  
    if err != nil {  
        log.Fatal(err)  
    }  

    // 3. 解析配置  
    err = yaml.Unmarshal([]byte(content), &appConfig)  
    if err != nil {  
        log.Fatal(err)  
    }  

    // 4. 监听配置变化  
    err = configClient.ListenConfig(vo.ConfigParam{  
        DataId: "application.yml",  
        Group:  "DEFAULT_GROUP",  
        OnChange: func(namespace, group, dataId, data string) {  
            log.Println("配置发生变化,重新加载...")  
            yaml.Unmarshal([]byte(data), &appConfig)  
        },  
    })  
    if err != nil {  
        log.Fatal(err)  
    }  

    // 5. 创建服务发现客户端并注册服务  
    namingClient, err := clients.NewNamingClient(  
        vo.NacosClientParam{  
            ClientConfig:  &cc,  
            ServerConfigs: sc,  
        },  
    )  
    if err != nil {  
        log.Fatal(err)  
    }  

    // 注册服务  
    _, err = namingClient.RegisterInstance(vo.RegisterInstanceParam{  
        Ip:          "127.0.0.1",  
        Port:        uint64(appConfig.Server.Port),  
        ServiceName: appConfig.Server.Name,  
        Weight:      10,  
        Enable:      true,  
        Healthy:     true,  
        Ephemeral:   true,  
    })  
    if err != nil {  
        log.Fatal(err)  
    }  

    // 6. 启动 Web 服务  
    r := gin.Default()  

    r.GET("/config", func(c *gin.Context) {  
        c.JSON(200, gin.H{  
            "message": appConfig.Message,  
            "port":    appConfig.Server.Port,  
            "name":    appConfig.Server.Name,  
        })  
    })  

    r.GET("/health", func(c *gin.Context) {  
        c.JSON(200, gin.H{"status": "UP"})  
    })  

    log.Printf("服务启动在端口 %d\n", appConfig.Server.Port)  
    r.Run(fmt.Sprintf(":%d", appConfig.Server.Port))  
}

对应的 Nacos 配置(application.yml):

server:  
  port: 8080  
  name: demo-service  

message: "Hello from Nacos Config!"

五、项目结构建议

your-project/  
├── main.go  
├── config/  
│   └── nacos.go          # Nacos 客户端初始化  
├── model/  
│   └── config.go         # 配置结构体  
└── service/  
    └── user_service.go   # 业务逻辑

config/nacos.go

package config  

import (  
    "github.com/nacos-group/nacos-sdk-go/v2/clients"  
    "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"  
    "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"  
    "github.com/nacos-group/nacos-sdk-go/v2/common/constant"  
    "github.com/nacos-group/nacos-sdk-go/v2/vo"  
)  

type NacosClient struct {  
    ConfigClient config_client.IConfigClient  
    NamingClient naming_client.INamingClient  
}  

func NewNacosClient() (*NacosClient, error) {  
    sc := []constant.ServerConfig{{  
        IpAddr: "localhost",  
        Port:   8848,  
    }}  

    cc := constant.ClientConfig{  
        NamespaceId: "",  
        TimeoutMs:   5000,  
        Username:    "nacos",  
        Password:    "nacos",  
    }  

    configClient, err := clients.NewConfigClient(  
        vo.NacosClientParam{  
            ClientConfig:  &cc,  
            ServerConfigs: sc,  
        },  
    )  
    if err != nil {  
        return nil, err  
    }  

    namingClient, err := clients.NewNamingClient(  
        vo.NacosClientParam{  
            ClientConfig:  &cc,  
            ServerConfigs: sc,  
        },  
    )  
    if err != nil {  
        return nil, err  
    }  

    return &NacosClient{  
        ConfigClient: configClient,  
        NamingClient: namingClient,  
    }, nil  
}

六、常用操作

删除配置

success, err := configClient.DeleteConfig(vo.ConfigParam{  
    DataId: "test-config.yml",  
    Group:  "DEFAULT_GROUP",  
})

注销服务

success, err := namingClient.DeregisterInstance(vo.DeregisterInstanceParam{  
    Ip:          "127.0.0.1",  
    Port:        8080,  
    ServiceName: "user-service",  
    GroupName:   "DEFAULT_GROUP",  
})

获取一个健康实例(负载均衡)

instance, err := namingClient.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{  
    ServiceName: "user-service",  
    GroupName:   "DEFAULT_GROUP",  
})

七、注意事项

  1. 命名空间 ID: 如果使用 public 命名空间,NamespaceId 留空字符串
  2. 服务注销: 程序退出时记得注销服务
  3. 心跳机制: Nacos 会自动发送心跳,保持服务健康状态
  4. 配置缓存: SDK 会在本地缓存配置,即使 Nacos 服务不可用也能读取
评论