golang环境变量、配置文件和标签初始化配置结构体插件库configuration的使用
Golang环境变量、配置文件和标签初始化配置结构体插件库configuration的使用
概述
Configuration是一个用于将值递归注入结构体的库,是设置配置对象的便捷方式。主要特性包括:
- 为结构体字段设置默认值 -
NewDefaultProvider()
- 从环境变量设置值 -
NewEnvProvider()
- 从命令行标志设置值 -
NewFlagProvider()
- 从JSON文件设置值 -
NewJSONFileProvider("./testdata/input.json")
支持的类型
string
,*string
,[]string
,[]*string
bool
,*bool
,[]bool
,[]*bool
- 所有整数类型及其切片
- 所有无符号整数类型及其切片
- 浮点类型及其切片
time.Duration
(支持如12ms
,2s
等格式)- 嵌入式结构体和结构体指针
- 任何实现了
FieldSetter
接口的自定义类型
快速开始
导入路径:github.com/BoRuDar/configuration/v5
示例代码
// 定义配置结构体
type Conf struct {
Name string `flag:"name"`
LastName string `default:"defaultLastName"`
Age byte `env:"AGE_ENV" default:"-1"`
BoolPtr *bool `default:"false"`
ObjPtr *struct {
F32 float32 `default:"32"`
StrPtr *string `default:"str_ptr_test"`
HundredMS time.Duration `default:"100ms"` // nolint:stylecheck
}
Obj struct {
IntPtr *int16 `default:"123"`
Beta int `file_json:"inside.beta" default:"24"`
StrSlice []string `default:"one;two"`
IntSlice []int64 `default:"3; 4"`
unexported string // 未导出字段会被忽略
}
URLs []*string `default:"http://localhost:3000;1.2.3.4:8080"`
HostIP ipTest `default:"127.0.0.3"`
}
// 初始化配置,执行顺序会被保留
cfg, err := New[Conf]( // 指定返回的结构体类型[T]
NewFlagProvider(), // 第1个执行
NewEnvProvider(), // 第2个执行
NewJSONFileProvider(fileName), // 第3个执行
NewDefaultProvider(), // 第4个执行
)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
如果只需要环境变量和默认值,可以使用简写形式:
cfg, err := configuration.FromEnvAndDefault[T]()
提供者(Providers)
可以指定一个或多个提供者,它们会按照定义的顺序执行:
[]Provider{
NewFlagProvider(), // 1
NewEnvProvider(), // 2
NewDefaultProvider(), // 3
}
重要:如果提供者成功设置了值,后续的提供者将不会执行。如果没有任何提供者能设置值,将返回错误。
自定义提供者
可以定义自定义提供者,需要实现以下接口:
type Provider interface {
Name() string
Tag() string
Init(ptr any) error
Provide(field reflect.StructField, v reflect.Value) error
}
默认提供者
查找default
标签并从中设置值:
struct {
// ...
Name string `default:"defaultName"`
// ...
}
环境变量提供者
查找env
标签并尝试从环境变量中获取值:
struct {
// ...
Age byte `env:"AGE"`
// ...
}
环境变量名称必须全部大写:
bad_env_var_name=bad
Also_Bad_Env_Var_Name=bad
GOOD_ENV_VAR_NAME=good
标志提供者
查找flag
标签并尝试从命令行标志设置值:
struct {
// ...
Name string `flag:"first_name|default_value|Description"`
// ...
}
default_value
和description
部分是可选的,可以省略。
JSON文件提供者
需要file_json:"<path_to_json_field>"
标签:
NewJSONFileProvider("./testdata/input.json")
例如,标签file_json:"cache.retention"
假设你的JSON文件结构如下:
{
"cache": {
"retention": 1
}
}
FieldSetter接口
可以定义如何设置任何自定义类型的字段:
type FieldSetter interface {
SetField(field reflect.StructField, val reflect.Value, valStr string) error
}
示例:
type ipTest net.IP
func (it *ipTest) SetField(_ reflect.StructField, val reflect.Value, valStr string) error {
i := ipTest(net.ParseIP(valStr))
if val.Kind() == reflect.Pointer {
val.Set(reflect.ValueOf(&i))
} else {
val.Set(reflect.ValueOf(i))
}
return nil
}
更多关于golang环境变量、配置文件和标签初始化配置结构体插件库configuration的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang环境变量、配置文件和标签初始化配置结构体插件库configuration的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 配置管理:环境变量、配置文件和标签初始化
在 Go 项目中,管理配置通常涉及多种方式,包括环境变量、配置文件和结构体标签。下面我将介绍这些方法以及如何使用流行的配置管理库。
1. 环境变量配置
使用标准库 os
可以轻松读取环境变量:
package main
import (
"fmt"
"os"
)
func main() {
// 设置环境变量(通常在系统或容器中设置)
os.Setenv("APP_PORT", "8080") // 仅为演示,实际不应在代码中设置
// 获取环境变量
port := os.Getenv("APP_PORT")
if port == "" {
port = "3000" // 默认值
}
fmt.Printf("Server will run on port: %s\n", port)
}
2. 配置文件解析
JSON 配置文件
package main
import (
"encoding/json"
"fmt"
"os"
)
type Config struct {
Database struct {
Host string `json:"host"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
} `json:"database"`
Server struct {
Port int `json:"port"`
} `json:"server"`
}
func main() {
file, err := os.Open("config.json")
if err != nil {
panic(err)
}
defer file.Close()
var config Config
decoder := json.NewDecoder(file)
err = decoder.Decode(&config)
if err != nil {
panic(err)
}
fmt.Printf("Database host: %s\n", config.Database.Host)
fmt.Printf("Server port: %d\n", config.Server.Port)
}
YAML 配置文件
需要先安装 gopkg.in/yaml.v3
包:
package main
import (
"fmt"
"os"
"gopkg.in/yaml.v3"
)
type Config struct {
Database struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
} `yaml:"database"`
Server struct {
Port int `yaml:"port"`
} `yaml:"server"`
}
func main() {
data, err := os.ReadFile("config.yaml")
if err != nil {
panic(err)
}
var config Config
err = yaml.Unmarshal(data, &config)
if err != nil {
panic(err)
}
fmt.Printf("Database host: %s\n", config.Database.Host)
fmt.Printf("Server port: %d\n", config.Server.Port)
}
3. 使用结构体标签初始化配置
结合环境变量和结构体标签可以创建灵活的配置系统:
package main
import (
"fmt"
"os"
"reflect"
"strconv"
"strings"
)
type Config struct {
Database struct {
Host string `env:"DB_HOST" default:"localhost"`
Port int `env:"DB_PORT" default:"5432"`
Username string `env:"DB_USER" default:"postgres"`
Password string `env:"DB_PASS" default:""`
}
Server struct {
Port int `env:"APP_PORT" default:"8080"`
}
}
func LoadConfig(cfg interface{}) error {
val := reflect.ValueOf(cfg).Elem()
return parseConfig(val)
}
func parseConfig(val reflect.Value) error {
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := typ.Field(i)
// 如果是嵌套结构体,递归处理
if field.Kind() == reflect.Struct {
if err := parseConfig(field); err != nil {
return err
}
continue
}
// 获取标签
envTag := fieldType.Tag.Get("env")
defaultTag := fieldType.Tag.Get("default")
// 从环境变量获取值
envValue := os.Getenv(envTag)
if envValue == "" {
envValue = defaultTag
}
// 根据字段类型设置值
switch field.Kind() {
case reflect.String:
field.SetString(envValue)
case reflect.Int:
intValue, err := strconv.Atoi(envValue)
if err != nil {
return fmt.Errorf("invalid integer value for %s: %v", envTag, err)
}
field.SetInt(int64(intValue))
case reflect.Bool:
boolValue, err := strconv.ParseBool(strings.ToLower(envValue))
if err != nil {
return fmt.Errorf("invalid boolean value for %s: %v", envTag, err)
}
field.SetBool(boolValue)
}
}
return nil
}
func main() {
var config Config
if err := LoadConfig(&config); err != nil {
panic(err)
}
fmt.Printf("Database config: %+v\n", config.Database)
fmt.Printf("Server config: %+v\n", config.Server)
}
4. 使用配置管理库
使用 viper 库
Viper 是一个流行的配置解决方案,支持多种配置源:
package main
import (
"fmt"
"log"
"github.com/spf13/viper"
)
type Config struct {
Database struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
} `mapstructure:"database"`
Server struct {
Port int `mapstructure:"port"`
} `mapstructure:"server"`
}
func main() {
viper.SetConfigName("config") // 配置文件名称(不带扩展名)
viper.SetConfigType("yaml") // 配置文件类型
viper.AddConfigPath(".") // 配置文件路径
// 读取环境变量
viper.AutomaticEnv()
viper.SetEnvPrefix("APP") // 设置环境变量前缀
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
// 设置默认值
viper.SetDefault("database.host", "localhost")
viper.SetDefault("database.port", 5432)
viper.SetDefault("server.port", 8080)
// 读取配置文件
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
log.Println("Config file not found, using defaults and environment variables")
} else {
log.Fatalf("Error reading config file: %v", err)
}
}
var config Config
if err := viper.Unmarshal(&config); err != nil {
log.Fatalf("Unable to decode into struct: %v", err)
}
fmt.Printf("Database config: %+v\n", config.Database)
fmt.Printf("Server config: %+v\n", config.Server)
}
使用 koanf 库
Koanf 是另一个灵活的配置管理库:
package main
import (
"fmt"
"log"
"github.com/knadh/koanf"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/env"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/providers/structs"
)
type Config struct {
Database struct {
Host string `koanf:"host"`
Port int `koanf:"port"`
Username string `koanf:"username"`
Password string `koanf:"password"`
} `koanf:"database"`
Server struct {
Port int `koanf:"port"`
} `koanf:"server"`
}
func main() {
var k = koanf.New(".")
// 加载默认值
if err := k.Load(structs.Provider(Config{
Database: struct {
Host string
Port int
Username string
Password string
}{
Host: "localhost",
Port: 5432,
Username: "postgres",
},
Server: struct {
Port int
}{
Port: 8080,
},
}, "koanf"), nil); err != nil {
log.Fatalf("Error loading defaults: %v", err)
}
// 加载配置文件
if err := k.Load(file.Provider("config.yaml"), yaml.Parser()); err != nil {
log.Printf("Error loading config file: %v", err)
}
// 加载环境变量
if err := k.Load(env.Provider("APP_", ".", func(s string) string {
return strings.Replace(strings.ToLower(
strings.TrimPrefix(s, "APP_")), "_", ".", -1)
}), nil); err != nil {
log.Printf("Error loading env vars: %v", err)
}
var config Config
if err := k.Unmarshal("", &config); err != nil {
log.Fatalf("Error unmarshaling config: %v", err)
}
fmt.Printf("Database config: %+v\n", config.Database)
fmt.Printf("Server config: %+v\n", config.Server)
}
最佳实践建议
- 配置优先级:通常环境变量 > 配置文件 > 默认值
- 敏感信息:密码等敏感信息应通过环境变量或密钥管理系统提供
- 配置验证:加载配置后应进行验证
- 热重载:生产环境中可能需要配置热重载功能
- 文档:为所有配置选项提供清晰的文档
以上方法可以根据项目需求组合使用,构建灵活可靠的配置管理系统。