Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-config

中文 | English

介绍

通过 gRPC 实现的简单 Go 远程配置库。

基于 C/S 架构,客户端和服务端之间通过 gRPC 进行通信。

由服务端 Manager 集中管理配置,在 Manager 启动前,注册每一个配置到 Manager 中。

客户端 LocalManager 负责从服务端 Manager 获取配置。

支持实时监听配置更新。

使用

引入

go get github.com/l0neman/go-config

服务端代码示例

func TestManager() {
    log.If(configManagerLogTag, "version: %s", GetVersion())
    configManager, err := getConfigManager()
    if err != nil {
        log.Eln(configManagerLogTag, "初始化失败 > ", err)
        return
    }

    // 使用配置结构体注册配置,仅支持结构体指针和结构体数组指针
    err = configManager.RegisterConfig(
        &AppMaster{},
        &[]SwitchSsh{},
    )
    if err != nil {
        log.Eln(configManagerLogTag, "注册配置失败 > ", err)
        return
    }

    // 初始化配置管理器,设置模式
    err = configManager.Init(ModeDefault)
    if err != nil {
        log.Eln(configManagerLogTag, "初始化配置管理器失败 > ", err)
        return
    }

    // 使用配置对象来获取对应配置
    var masterConfig AppMaster
    err = configManager.GetConfig(&masterConfig)
    if err != nil {
        log.Eln(configManagerLogTag, "获取配置失败 >", err)
        return
    }

    log.If(configManagerLogTag, "master 配置 > %+v", masterConfig)

    var switchSshTest []SwitchSsh
    err = configManager.GetConfig(&switchSshTest)
    if err != nil {
        log.Eln(configManagerLogTag, "获取配置失败 >", err)
        return
    }

    log.If(configManagerLogTag, "switch ssh config 配置 > %+v", switchSshTest)

    err = configManager.ClearConfig(&switchSshTest)
    if err != nil {
        log2.Println("清除配置失败 >", err)
    }
	
    startService(configManager)
}

func startService(configManager ConfigManager) {
    // 启动配置服务
    listener, err := net.Listen("tcp", ":8772")
    if err != nil {
        log.Eln(configManagerLogTag, "监听失败 > ", err)
        return
    }

    log.Df(configManagerLogTag, "8772 端口监听启动")
    server := grpc.NewServer()
    // 注册配置服务到 gRPC 服务上
    RegisterGrpcServer(configManager, server)
    err = server.Serve(listener)
    if err != nil {
        log.Eln(configManagerLogTag, err)
    }
}

// helper

func getConfigManager() (ConfigManager, error) {
    EnableDebug()
    // 创建配置服务端,提供数据库连接参数 (支持 MySQL 或 SQLite)

    // MySQL 示例
    rdbms := NewMySqlConfig("127.0.0.1", "3306", "root", "123456")

    // SQLite 示例
    // rdbms := NewSqliteConfig("go_config.db")

    configManager, err := NewManager(NewConfig(rdbms))
    if err != nil {
        log.Eln(configManagerLogTag, "初始化失败 > ", err)
        return nil, err
    }

    return configManager, nil
}

客户端代码示例

func TestLocalManager() {
    log.Iln("version: ", GetVersion())

    localConfigManager := getLocalManager()
    // 注册配置
    _ = localConfigManager.RegisterConfig(&AppMaster{})
    // 初始化本地配置管理器
    err := localConfigManager.Init(ModeDefault)
    if err != nil {
        log.Eln(configManagerLogTag, "初始化失败 > ", err)
        return
    }

    appMaster := &AppMaster{}
    // 获取配置
    err = localConfigManager.GetConfig(appMaster)
    if err != nil {
        log.Eln(configManagerLogTag, "获取配置失败 >", err)
        return
    }

    log.If(configManagerLogTag, "appMaster 配置 > %+v", appMaster)

    appMaster.Data.Influx = "1.1.1.1:8086"

    // 更新配置
    err = localConfigManager.SetConfig(appMaster)
    if err != nil {
        log.Eln(configManagerLogTag, "写入配置失败 >", err)
    }
    
    err = localConfigManager.GetConfig(appMaster)
    if err != nil {
        log.Eln(configManagerLogTag, "获取配置失败 >", err)
        return
    }

    log.If(configManagerLogTag, "appMaster 配置 2 > %+v", appMaster)
}

func getLocalManager() ConfigManager {
    EnableDebug()
    // 创建配置客户端,提供服务端地址,默认端口 8772,支持自定义端口,例如 0.0.0.0:8888
    localConfigManager := NewLocalManager("0.0.0.0")
    return localConfigManager
}

Mode 说明

对于服务端 Manager 来说,远程代表数据库 (MySQL 或 SQLite)
对于客户端 LocalManager 来说,远程代表 Manager

ModeDefault 默认模式,首先从远程获取配置,获取不到就从本地获取配置,
本地获取不到就生成配置模板并退出程序,从本地获取到配置,会写入远程(如果远程是数据库)。
SetConfig 会写入远程

ModeRemote 仅从远程获取配置,获取不到就报错
SetConfig  会写入远程

ModeLocal 仅从本地获取配置,获取不到就报错
SetConfig 不会写入远程

配置结构体说明

要支持从 Manager 获取某个配置,需要先注册配置。

默认以结构体名称转化为小写加下划线作为配置的唯一 key,切片或数组类型则以类型增加 _list 后缀为唯一 key。

也可以为结构体提供 Key 函数来自定义配置的 key。

每一个基本类型的配置字段都支持使用 default 标签设置默认值。

默认值会用于生成默认配置文件,以及和 GetConfig 时生效,为空的字段会被填充默认值。

下面是 master 的配置结构体示例。

type AppMaster struct {
    Flag struct {
        AppMode       string `yaml:"app_mode" default:"ib"`
        SwitchType    string `yaml:"switch_type" default:"ruijie"`
        HttpPort      string `yaml:"http_port" default:"8765"`
        IbmStorageApi string `yaml:"ibm_storage_api" default:"https://{host}:{port}/scalemgmt/v2/filesystems;{username}/{password}"`
    } `yaml:"flag"`

    Metric struct {
        RunPowerVal     int `yaml:"run_power_val" default:"80"`
        OneServerGpuCnt int `yaml:"one_server_gpu_cnt" default:"8"`
    } `yaml:"metric"`

    Debug struct {
        Log   bool `yaml:"log" default:"false"`
        Pprof bool `yaml:"pprof" default:"false"`
    } `yaml:"debug"`

    Data struct {
        TimeSeriesDb     string `yaml:"time_series_db" default:"influxdb"`
        Influx           string `yaml:"influx" default:"{host}:{port}"`
        InfluxPeriodDays int    `yaml:"influx_period_days" default:"2"`
        Taos             string `yaml:"taos" default:"{host}:{port}@{user}/{pwd}"`
    } `yaml:"data"`

    Report struct {
        Feishu          string `yaml:"feishu" default:"{feishu_link}"`
        Trap            string `yaml:"trap" default:"{host}:{port}"`
        ServerFanMinRpm int    `yaml:"server_fan_min_rpm" default:"-1"`
        CheckDisk       string `yaml:"check_disk" default:"/"`
    } `yaml:"report"`

    Legacy struct {
        Master   string `yaml:"master" default:"{host}"`
        UseOldDb bool   `yaml:"use_old_db" default:"false"`
    } `yaml:"legacy"`

    Collector struct {
        SonicSwitch         bool `yaml:"sonic_switch" default:"false"`
        SonicSwitchGrpcPort int  `yaml:"sonic_switch_grpc_port" default:"-1"`
    } `yaml:"collector"`

    Email struct {
        Sender string `yaml:"sender" default:"{email}"`
    } `yaml:"email"`
}

func (v *AppMaster) Key() string {
    return "go_master_new"
}

相关 API

// 获取版本号
GetVersion()
// 开启调试
EnableDebug() 

// 创建 MySQL 配置
NewMySqlConfig(host, port, user, pwd)
// 创建 SQLite 配置
NewSqliteConfig(path)
// 创建管理器配置包装
NewConfig(rdbms)
// 创建服务端管理器
NewManager(config)

ConfigManager
// RegisterConfig 注册配置,提供结构体指针
// providers 配置结构体对象,用于提供 key 及默认值
RegisterConfig(providers ...any) error
// UnregisterConfig 解注册配置
UnregisterConfig(providers ...any) error

// Init 初始化 Manager,需在获取/设置配置之前调用
Init(mode Mode) error
// GetConfig 提供结构体接收指针,获取配置
GetConfig(receiver any) error
// SetConfig 提供结构体指针,设置配置
SetConfig(config any) error
// ClearConfig 提供结构体指针,清除配置
ClearConfig(provider any) error
// WatchConfig 监听配置变化
// provider 配置结构体对象,callback 用于回调配置
WatchConfig(provider any, callback func(config any)) (canceler func(), err error)

// gRPC 服务器注册
RegisterGrpcServer(manager *Manager, server *grpc.Server)

配置变更感知

服务端测试代码

func TestManager() {
    configManager, err := getConfigManager()
    if err != nil {
        log.Eln(configManagerLogTag, "初始化失败 > ", err)
        return
    }

    // 注册配置
    err = configManager.RegisterConfig(
        &AppMaster{},
        &[]SwitchSsh{},
    )
    if err != nil {
        log.Eln(configManagerLogTag, "注册配置失败 > ", err)
        return
    }

    // 初始化配置管理器
    err = configManager.Init(ModeDefault)
    if err != nil {
        log.Eln(configManagerLogTag, "初始化失败 > ", err)
        return
    }

    var masterConfig AppMaster
    // 获取配置
    err = configManager.GetConfig(&masterConfig)
    if err != nil {
        log.Eln(configManagerLogTag, "获取配置失败 >", err)
        return
    }

    log.If(configManagerLogTag, "master 配置 > %+v", masterConfig)

    // 监听配置变更
    canceler, err := configManager.WatchConfig(&AppMaster{}, func(config any) {
        // 转换为对应的配置指针
        master := config.(*AppMaster)
        log.If(configManagerLogTag, "AppMaster 配置变更 > %+v", master.Flag)
    })
    defer canceler()
    if err != nil {
        log.Eln(configManagerLogTag, "监听配置失败 > ", err)
        return
    }

    // 默认配置变化的情况
    go func(manager ConfigManager) {
        time.Sleep(time.Second)
        i := 1
        for {
            var appMaster AppMaster
            appMaster.Flag.HttpPort = fmt.Sprintf("%d", 8772+i)
            // 更新配置后,将会通知到监听器
            err := manager.SetConfig(&appMaster)
            if err != nil {
                log.Eln(configManagerLogTag, err)
            }

            log.Df(configManagerLogTag, "AppMaster 配置设置 > %+v", appMaster.Flag)
            i++
            time.Sleep(time.Second * 2)
        }
    }(configManager)

    // 注册 gRPC 配置
    startService(configManager)
}

客户端测试代码

本地动态配置管理器分发时,消息不会通知到服务端的 Listener,只会通知到本地注册的 Listener。

func TestLocalManager() {
    // 获取本地配置管理器
    localManager := getLocalManager()
    // 注册配置
    err := localManager.RegisterConfig(&AppMaster{})
    if err != nil {
        log.Eln(configManagerLogTag, "注册配置失败 > ", err)
        return
    }

    // 初始化配置
    err = localManager.Init(ModeDefault)
    if err != nil {
        log.Eln(configManagerLogTag, "初始化失败 > ", err)
        return
    }

    // 获取配置
    var appMaster AppMaster
    err = localManager.GetConfig(&appMaster)
    if err != nil {
        log.Eln(configManagerLogTag, "获取配置失败 > ", err)
        return
    }

    log.Df(configManagerLogTag, "AppMaster 配置 > %+v", appMaster)

    // 监听配置变更
    canceler, err := localManager.WatchConfig(&AppMaster{}, func(config any) {
        // 转换为对应的配置指针
        master := config.(*AppMaster)
        log.If(configManagerLogTag, "AppMaster 配置变更 > %+v", master.Flag.HttpPort)
    })
    defer func() {
        log.Df(configManagerLogTag, "AppMaster 配置变更 取消监听")
        canceler()
    }()
    if err != nil {
        log.Eln(configManagerLogTag, "监听配置失败 > ", err)
        return
    }

    select {}
}

原理和流程

序列化与存储过程

config_manager_parser.png

配置获取流程

config_manager_flow.png

监听配置变更流程

config_watch_flow.png

About

A simple Go remote configuration library implemented via gRPC.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages