一个应用程序可能从多个资源源获取配置。例如,应用程序可能从环境变量获取配置,从文件获取配置,从配置服务(例如: consul、nacos等)获取配置等。
Leo的config包就是帮助开发者,从多中资源中加载配置和监听配置。
Leo当前内置了四种源开箱即用:
Leo当前支持了四种常用的配置格式:
syntax = "proto3";
package leo.config.example;
option go_package = "github.com/go-leo/config/example/configs;configs";
import "leo/config/annotations.proto";
message Application {
option (leo.config.enable) = true;
string LEO_RUN_ENV = 1;
GRPC grpc = 2;
Redis redis = 4;
}
message GRPC {
string addr = 1;
int32 port = 2;
}
message Redis {
string network = 1;
string addr = 2;
string password = 3;
int32 db = 4;
}
message上添加option (leo.config.enable) = true;
注解,表明该message是配置结构,没有该注解,则不会被解析为配置结构。
protoc \
--proto_path=. \
--proto_path=../proto \
--go_out=. \
--go_opt=paths=source_relative \
--config_out=. \
--config_opt=paths=source_relative \
*/*.proto
// Code generated by protoc-gen-config. DO NOT EDIT.
package configs
import (
context "context"
config "github.com/go-leo/config"
resource "github.com/go-leo/config/resource"
proto "google.golang.org/protobuf/proto"
sync "sync"
)
var (
_ApplicationConfig = &Application{}
_ApplicationConfigMutex sync.RWMutex
)
func GetApplicationConfig() *Application {
_ApplicationConfigMutex.RLock()
cloned := proto.Clone(_ApplicationConfig)
_ApplicationConfigMutex.RUnlock()
return cloned.(*Application)
}
func SetApplicationConfig(conf *Application) {
cloned := proto.Clone(conf).(*Application)
_ApplicationConfigMutex.Lock()
_ApplicationConfig = cloned
_ApplicationConfigMutex.Unlock()
}
func LoadApplicationConfig(ctx context.Context, resources ...resource.Resource) error {
conf, err := config.Load[*Application](ctx, resources...)
if err != nil {
return err
}
SetApplicationConfig(conf)
return nil
}
func WatchApplicationConfig(ctx context.Context, resources ...resource.Resource) (<-chan struct{}, func(context.Context) error, error) {
notifyC := make(chan *Application)
errC := make(chan error)
stop, err := config.Watch(ctx, notifyC, errC, resources...)
if err != nil {
return nil, nil, err
}
changedC := make(chan struct{}, 1)
go func() {
for {
select {
case <-ctx.Done():
return
case conf := <-notifyC:
SetApplicationConfig(conf)
select {
case <-ctx.Done():
return
case changedC <- struct{}{}:
default:
}
}
}
}()
return changedC, stop, nil
}
package main
import (
"context"
"fmt"
"os"
"time"
"github.com/go-leo/config/example/configs"
"github.com/go-leo/config/resource/env"
"github.com/go-leo/config/resource/file"
)
func main() {
// prepare config
// 模拟环境变量
os.Setenv("LEO_RUN_ENV", "dev")
defer os.Unsetenv("LEO_RUN_ENV")
tmpDir := os.TempDir()
// 模拟json文件
jsonFilename := tmpDir + "/config.json"
if err := os.WriteFile(jsonFilename, genConfigJSON(), 0o644); err != nil {
panic(err)
}
defer os.Remove(jsonFilename)
// 模拟yaml文件
yamlFilename := tmpDir + "/config.yaml"
if err := os.WriteFile(yamlFilename, genConfigYaml(), 0o644); err != nil {
panic(err)
}
defer os.Remove(yamlFilename)
// 创建环境变量资源
envRsc, err := env.New("LEO_")
if err != nil {
panic(err)
}
// 创建json文件资源
jsonRsc, err := file.New(jsonFilename)
if err != nil {
panic(err)
}
// 创建yaml文件资源
yamlRsc, err := file.New(yamlFilename)
if err != nil {
panic(err)
}
// 加载配置
if err := configs.LoadApplicationConfig(context.TODO(), envRsc, jsonRsc, yamlRsc); err != nil {
panic(err)
}
// 获取配置
fmt.Println(configs.GetApplicationConfig())
// 监听配置
// sigC 当有配置更新时,会发送通知。
// stop 用于停止监听。
sigC, stop, err := configs.WatchApplicationConfig(context.TODO(), envRsc, jsonRsc, yamlRsc)
if err != nil {
panic(err)
}
go func() {
time.Sleep(10 * time.Second)
stop(context.TODO())
}()
go func() {
for range sigC {
fmt.Println(configs.GetApplicationConfig())
}
}()
go func() {
for {
time.Sleep(time.Second)
if err := os.WriteFile(jsonFilename, genConfigJSON(), 0o644); err != nil {
panic(err)
}
if err := os.WriteFile(yamlFilename, genConfigYaml(), 0o644); err != nil {
panic(err)
}
}
}()
time.Sleep(11*time.Second)
}
func genConfigJSON() []byte {
return []byte(fmt.Sprintf(`{"grpc":{"addr":"127.0.0.1","port":%d}}`, time.Now().Unix()))
}
func genConfigYaml() []byte {
return []byte(fmt.Sprintf(`
redis:
addr: 127.0.0.1:6379
network: tcp
password: oqnevaqm
db: %d`, time.Now().Unix()))
}
详细代码见config