使用 Viper 和 Nacos 配置中心实现动态配置

1,704 阅读4分钟

前言

在网上没有找到 Viper 搭配 Nacos 配置中心的合适例子,大多是单独的 Viper 或者 SpringCloud Nacos 配置,于是就自己实现了一个简单的适用 Go 的 Example,希望可以给大家提供一个参考。

项目地址在这里,建议 clone 下来对着代码跟着本文走以获得更好的阅读体验。

注意:本例的 Nacos 等相关配置进行了简化,更多配置请参考官方文档

思路

Example 提供了两种思路:

  • 思路一

    较为常见的一种思路

    1. 在Nacos 中配置好项目所需配置,例如 MySQL 等;
    2. 在项目本地配置好 Nacos 的地址端口等信息;
    3. 建立连接获取第一步配置在 Naocs 中的 MySQL 等配置;
    4. 监听 Nacos 配置变化

    这种思路对应 Example 中的 remote 模块

  • 思路二

    奇奇怪怪的思路

    1. 在项目本地配置好所有所需配置(Nacos, MySQL等)
    2. 项目启动将本地配置发布到 Nacos;
    3. 监听 Nacos 配置,如果发生变化应用到本地配置文件;

    这种思路对应 Example 中的 local 模块

代码实现

思路一

  • 配置好配置文件及其对应的映射结构体

    配置结构体,由于 Example 中在 Nacos 上配置了 MySQL 所以这里需要配置 MySQL 结构体,详见 readme

    type Config struct {
    	Nacos NacosConfig
    	MySQL MySQLConfig
    }
    
    type NacosConfig struct {
    	Host   string `json:"host"`
    	Port   int    `json:"port"`
    	DataId string `json:"dataId"`
    	Group  string `json:"group"`
    }
    
    type MySQLConfig struct {
    	Host     string `json:"host"`
    	Port     int    `json:"port"`
    	Username string `json:"username"`
    	Password string `json:"password"`
    	Database string `json:"database"`
    }
    

    配置文件

    nacos:
        host: 127.0.0.1
        port: 8848
        dataid: example-remote
        group: example-remote
    
  • 使用 Viper 读取本地 Nacos 配置

    viper.SetConfigFile(configPath)
    // read nacosConfig
    err := viper.ReadInConfig()
    if err != nil {
        log.Fatalf("viper read nacosConfig failed: %v", err)
    }
    err = viper.Unmarshal(&config)
    if err != nil {
        log.Fatalf("viper unmarshal nacosConfig failed: %v", err)
    }
    viper.WatchConfig()
    
  • 配置 Nacos Server 和 Client 配置

    // server nacosConfig
    sc := []constant.ServerConfig{
        *constant.NewServerConfig(config.Nacos.Host, uint64(config.Nacos.Port), constant.WithContextPath("/nacos")),
    }
    // client nacosConfig
    cc := *constant.NewClientConfig(
        constant.WithNamespaceId(""),
    )
    
  • 配置 Nacos 配置管理客户端

    // client
    client, err := clients.NewConfigClient(
        vo.NacosClientParam{
            ClientConfig:  &cc,
            ServerConfigs: sc,
        },
    )
    if err != nil {
        log.Fatalf("create nacos nacosConfig client failed: %v", err)
    }
    
  • 获取 Nacos 中的配置并利用 Viper 对结构体进行赋值

    content, err := client.GetConfig(vo.ConfigParam{
        DataId: config.Nacos.DataId,
        Group:  config.Nacos.Group,
    })
    err = viper.ReadConfig(bytes.NewBufferString(content))
    if err != nil {
        log.Fatal("viper read config failed: ", err)
    }
    err = viper.Unmarshal(&config)
    if err != nil {
        log.Fatalf("viper unmarshal config failed: %v", err)
    }
    
  • 监听 Nacos 配置变化,如果发生变化利用 Viper 应用到本地

      err = client.ListenConfig(vo.ConfigParam{
         DataId: config.Nacos.DataId,
         Group:  config.Nacos.Group,
         OnChange: func(namespace, group, dataId, data string) {
            err := viper.ReadConfig(bytes.NewBufferString(data))
            if err != nil {
               log.Printf("viper read config failed: %v", err)
            }
            err = viper.Unmarshal(&config)
            if err != nil {
               log.Printf("viper unmarshal config failed: %v", err)
            }
            log.Println(config)
         },
      })
      if err != nil {
         log.Fatalf("nacos listen config failed: %v", err)
      }
    

思路二

  • 在本地配置文件配置好所有配置及其对应的结构体

    配置结构体

    type Config struct {
    	Service ServiceConfig
    	Nacos   NacosConfig
    	MySQL   MySQLConfig
    }
    
    type ServiceConfig struct {
    	Name string `json:"name"`
    	Addr string `json:"addr"`
    }
    
    type NacosConfig struct {
    	Host   string `json:"host"`
    	Port   int    `json:"port"`
    	DataId string `json:"dataId"`
    	Group  string `json:"group"`
    }
    
    type MySQLConfig struct {
    	Host     string `json:"host"`
    	Port     int    `json:"port"`
    	Username string `json:"username"`
    	Password string `json:"password"`
    	Database string `json:"database"`
    }
    

    配置文件

    mysql:
        database: example-local
        host: 127.0.0.1
        password: 114514
        port: 3306
        username: root
    nacos:
        dataid: example-local
        group: example-local
        host: 127.0.0.1
        port: 8848
    service:
        addr: 127.0.0.1:8080
        name: example-local
    
  • 利用 Viper 读取配置文件

    viper.SetConfigFile(configPath)
    // read config
    err := viper.ReadInConfig()
    if err != nil {
        log.Fatalf("viper read config failed: %v", err)
    }
    err = viper.Unmarshal(&config)
    if err != nil {
        log.Fatalf("viper unmarshal config failed: %v", err)
    }
    viper.WatchConfig()
    
  • 配置 Nacos Server 和 Client

    // server config
    sc := []constant.ServerConfig{
        *constant.NewServerConfig(config.Nacos.Host, uint64(config.Nacos.Port), constant.WithContextPath("/nacos")),
    }
    // client config
    cc := *constant.NewClientConfig(
        constant.WithNamespaceId(""),
    )
    
  • 配置 Nacos 配置管理客户端

    // client
    client, err := clients.NewConfigClient(
        vo.NacosClientParam{
            ClientConfig:  &cc,
            ServerConfigs: sc,
        },
    )
    if err != nil {
        log.Fatalf("create nacos config client failed: %v", err)
    }
    
  • 将本地配置发布到 Nacos

    jsonBytes, err := json.Marshal(config)
    if err != nil {
        log.Fatalf("marshal json config failed: %v", err)
    }
    _, err = client.PublishConfig(vo.ConfigParam{
        DataId:  config.Nacos.DataId,
        Group:   config.Nacos.Group,
        Content: string(jsonBytes),
    })
    if err != nil {
        log.Fatalf("nacos publish config failed: %v", err)
    }
    
  • 监听 Nacos 配置变化,发生变化利用 Viper 应用到本地配置文件

      err = client.ListenConfig(vo.ConfigParam{
         DataId: config.Nacos.DataId,
         Group:  config.Nacos.Group,
         OnChange: func(namespace, group, dataId, data string) {
            viper.SetConfigType(configType)
            err := viper.ReadConfig(bytes.NewBufferString(data))
            if err != nil {
               log.Printf("viper read config failed: %v", err)
            }
            err = viper.WriteConfigAs(configPath)
            log.Println("apply new config to local")
            if err != nil {
               log.Printf("viper write config failed: %v", err)
            }
         },
      })
      if err != nil {
         log.Fatalf("nacos listen config failed: %v", err)
      }
    

运行

思路一

启动 Nacos

docker-compose up

配置 Nacos

在浏览器上访问 127.0.0.1:8848/nacos

默认用户名和密码为 nacos

添加 MySQL 配置

  • Data ID: example-remote
  • Group: example-remote
mysql:
    host: 127.0.0.1
    port: 3306
    username: root
    password: 114514
    database: example-remote

启动

cd remote
go run .

动态配置

在 Nacos 更改 MySQL 配置,观察本地日志输出。

注意:程序默认 90s 后停止,请按需配置

思路二

启动 Nacos

docker-compose up

启动

cd local
go run .

动态配置

在浏览器上访问 127.0.0.1:8848/nacos

默认用户名和密码为 nacos

  • 查看本地配置发布情况
  • 更改 Nacos 配置,观察本地配置文件变化

注意:程序默认 90s 后停止,请按需配置

总结

以上就是本篇文章的所有内容了,希望可以帮助到你,欢迎 Star,如果有问题或疑问欢迎在评论区指出或者私聊,以上ww

参考列表