golang基于环境变量递归实例化配置结构体的插件库swap的使用
Golang基于环境变量递归实例化配置结构体的插件库Swap使用指南
简介
Swap是一个Golang库,可以根据构建环境动态实例化和配置结构体,以及递归解析配置文件到结构体中。它可以帮助你保持项目和配置文件的组织性和可维护性,同时提高生产力。
安装
import "github.com/oblq/swap"
快速开始
假设有以下项目结构:
├── config
│ ├── Tool1.yaml
│ ├── Tool1.production.yaml
│ ├── Tool2.yaml
│ ├── Tool3.yaml
│ └── Tool4.yaml
└── main.go
我们可以加载一个结构体,并根据当前构建环境使用正确的配置文件配置其字段:
// main.go
import (
"reflect"
"github.com/oblq/swap"
)
// 共享单例
var ToolBox struct {
// 默认情况下,swap会查找与结构体字段名相同的文件(Tool1.*,区分大小写)
// 可选地通过标签传递一个或多个配置文件名,
// 它们将被添加到工厂方法中传递的配置文件中
// 可以省略文件扩展名
Tool1 tools.ToolConfigurable
Tool2 tools.ToolWFactory `swap:"Tool3|Tool4"`
Tool3 tools.ToolRegistered
Tool4 tools.ToolNotRecognized
Nested1 struct {
Tool1 tools.ToolConfigurable
Nested2 struct {
Tool2 tools.ToolConfigurable
Nested3 struct {
Tool3 tools.ToolConfigurable
}
}
}
// 添加'-'值可以跳过该字段
OmittedTool tools.ToolConfigurable `swap:"-"`
}
func init() {
// 获取给定配置路径的新Builder实例
builder := swap.NewBuilder("./config")
// 为`tools.ToolRegistered`类型注册一个`FactoryFunc`
builder.RegisterType(reflect.TypeOf(tools.ToolRegistered{}),
func(configFiles ...string) (i interface{}, err error) {
instance := &tools.Tool{}
err = swap.Parse(&instance, configFiles...)
return instance, err
})
// 手动将当前构建环境强制设置为`production`
// 这样生产配置文件(如果存在)将与默认配置文件一起传递给构造函数/配置函数
// 但会在之后传递,因此它将覆盖先前传递的参数
builder.EnvHandler.SetCurrent("production")
// 构建ToolBox
if err := builder.Build(&ToolBox); err != nil {
panic(err)
}
}
Builder
Builder是一种Director模式的实现,它递归地构建和/或配置给定结构体的每个字段。它必须使用包含配置文件的路径进行初始化,并且可以选择使用自定义的EnvironmentHandler
:
// 获取给定配置路径的新Builder实例
builder := swap.NewBuilder("./config")
// 可选设置自定义EnvironmentHandler
envHandler := swap.NewEnvironmentHandler(swap.DefaultEnvs.Slice())
builder = builder.WithCustomEnvHandler(envHandler)
结构体字段可以通过以下方式自动"创建"或"配置":
- 实现
swap.Factory
接口:
// Factory是抽象工厂接口
type Factory interface {
New(configFiles ...string) (interface{}, error)
}
// 例如:
func (t *Tool) New(configFiles ...string) (i interface{}, err error) {
instance := &Tool{}
err = swap.Parse(&instance, configFiles...)
return instance, err
}
- 实现
swap.Configurable
接口:
// Configurable接口允许自动初始化为零值的字段的配置
type Configurable interface {
Configure(configFiles ...string) error
}
// 例如:
func (t *Tool) Configure(configFiles ...string) (err error) {
return swap.Parse(t, configFiles...)
}
- 为该特定字段类型注册了
swap.FactoryFunc
:
// 为`Tool`类型注册`FactoryFunc`
builder.RegisterType(reflect.TypeOf(Tool{}),
func(configFiles ...string) (i interface{}, err error) {
instance := &Tool{}
err = swap.Parse(&instance, configFiles...)
return instance, err
})
EnvironmentHandler
EnvironmentHandler使用环境列表([]*Environment
)初始化,当前环境通过将标签与其特定的正则表达式匹配来确定。
提供了五个标准构建环境以便使用:
// 默认环境配置
var DefaultEnvs = defaultEnvs{
Production: NewEnvironment("production", `(production)|(master)|(^v(0|[1-9]+)(\.(0|[1-9]+)+)?(\.(\*|(0|[1-9]+)+))?$)`),
Staging: NewEnvironment("staging", `(staging)|(release/*)|(hotfix/*)|(bugfix/*)`),
Testing: NewEnvironment("testing", `(testing)|(test)`),
Development: NewEnvironment("development", `(development)|(develop)|(dev)|(feature/*)`),
Local: NewEnvironment("local", `local`),
}
ConfigParser (无感知、分层配置解析)
Swap实现了两个配置解析函数:
swap.Parse()
swap.ParseByEnv()
两者都使用三个特定的结构体字段标签:
`swapcp:"default=<default_value>"`
提供一个默认值,如果解析的配置文件没有提供该值,将使用默认值`swapcp:"env=<system_environment_var_name>"`
将从环境变量中获取值(如果存在),覆盖配置文件提供的值和/或默认值`swapcp:"required"`
如果没有为该字段提供值,将返回错误
完整示例
package main
import (
"fmt"
"os"
"reflect"
"github.com/oblq/swap"
)
// 定义一个可配置的工具结构体
type Tool struct {
Name string `yaml:"name"`
Port int `yaml:"port"`
}
// 实现swap.Configurable接口
func (t *Tool) Configure(configFiles ...string) error {
return swap.Parse(t, configFiles...)
}
// 主配置结构体
var Config struct {
Database struct {
Host string `swapcp:"env=DB_HOST,default=localhost"`
Port int `swapcp:"env=DB_PORT,default=5432"`
User string `swapcp:"env=DB_USER,required"`
Password string `swapcp:"env=DB_PASSWORD,required"`
}
Tools struct {
Tool1 Tool
Tool2 Tool `swap:"custom_tool_config"`
}
}
func main() {
// 设置环境变量
_ = os.Setenv("DB_USER", "admin")
_ = os.Setenv("DB_PASSWORD", "secret")
// 创建builder实例
builder := swap.NewBuilder("./config")
// 注册Tool类型的工厂函数
builder.RegisterType(reflect.TypeOf(Tool{}),
func(configFiles ...string) (interface{}, error) {
instance := &Tool{}
err := swap.Parse(instance, configFiles...)
return instance, err
})
// 构建配置
if err := builder.Build(&Config); err != nil {
fmt.Printf("Error building config: %v\n", err)
return
}
// 打印配置
fmt.Printf("Database config: %+v\n", Config.Database)
fmt.Printf("Tool1 config: %+v\n", Config.Tools.Tool1)
fmt.Printf("Tool2 config: %+v\n", Config.Tools.Tool2)
}
假设有以下配置文件:
config/Database.yaml:
host: db.example.com
port: 3306
config/Tool1.yaml:
name: Tool One
port: 8080
config/custom_tool_config.yaml:
name: Custom Tool
port: 9090
运行程序后,输出将显示从配置文件和环境变量合并的配置值。
Swap是一个强大的配置管理工具,特别适合需要根据不同环境(开发、测试、生产等)动态加载配置的应用程序。
更多关于golang基于环境变量递归实例化配置结构体的插件库swap的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang基于环境变量递归实例化配置结构体的插件库swap的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 基于环境变量递归实例化配置结构体的插件库 Swap 使用指南
Swap 是一个用于 Golang 的轻量级配置管理库,它允许你通过环境变量递归地实例化配置结构体。下面我将详细介绍如何使用 Swap 库。
安装 Swap
首先,使用 go get 安装 Swap:
go get github.com/oblq/spr
注意:Swap 现在已更名为 Spr(Simple Plugin-based Recursive configuration),但功能相同。
基本用法
1. 定义配置结构体
type Config struct {
Database struct {
Host string `env:"DB_HOST" envDefault:"localhost"`
Port int `env:"DB_PORT" envDefault:"5432"`
Username string `env:"DB_USER" envDefault:"postgres"`
Password string `env:"DB_PASS" envDefault:""`
}
Server struct {
Port int `env:"SERVER_PORT" envDefault:"8080"`
}
}
2. 使用 Swap 加载配置
package main
import (
"fmt"
"github.com/oblq/spr"
"os"
)
func main() {
// 设置环境变量(实际使用时通常在外部设置)
os.Setenv("DB_HOST", "db.example.com")
os.Setenv("DB_PORT", "3306")
os.Setenv("SERVER_PORT", "9090")
var config Config
if err := spr.Parse(&config); err != nil {
panic(err)
}
fmt.Printf("Database Host: %s\n", config.Database.Host)
fmt.Printf("Database Port: %d\n", config.Database.Port)
fmt.Printf("Server Port: %d\n", config.Server.Port)
}
高级特性
1. 嵌套结构体支持
Swap 可以递归处理嵌套结构体:
type AuthConfig struct {
JWTSecret string `env:"JWT_SECRET" envDefault:"default-secret"`
}
type Config struct {
Database struct {
// ... 同上
}
Auth AuthConfig
}
2. 自定义解析器
你可以为特定类型注册自定义解析器:
type Duration time.Duration
func (d *Duration) UnmarshalText(text []byte) error {
dur, err := time.ParseDuration(string(text))
if err != nil {
return err
}
*d = Duration(dur)
return nil
}
type Config struct {
Timeout Duration `env:"TIMEOUT" envDefault:"5s"`
}
3. 必填字段验证
type Config struct {
APIKey string `env:"API_KEY,required"`
}
如果 API_KEY 环境变量未设置,Parse 会返回错误。
4. 切片支持
type Config struct {
AllowedIPs []string `env:"ALLOWED_IPS" envSeparator:","`
}
设置环境变量:
export ALLOWED_IPS="192.168.1.1,192.168.1.2,10.0.0.1"
5. 多环境配置
type Config struct {
Env string `env:"APP_ENV" envDefault:"development"`
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
// 根据环境使用不同的默认值
Debug bool `env:"DEBUG" envDefault:"true" envDefaultProduction:"false"`
}
实际应用示例
package main
import (
"fmt"
"github.com/oblq/spr"
"log"
"os"
)
type Config struct {
AppName string `env:"APP_NAME" envDefault:"MyApp"`
Debug bool `env:"DEBUG" envDefault:"false"`
Database struct {
Host string `env:"DB_HOST" envDefault:"localhost"`
Port int `env:"DB_PORT" envDefault:"5432"`
Name string `env:"DB_NAME" envDefault:"mydb"`
User string `env:"DB_USER" envDefault:"postgres"`
Password string `env:"DB_PASS" envDefault:""`
SSLMode string `env:"DB_SSL_MODE" envDefault:"disable"`
}
Redis struct {
Addr string `env:"REDIS_ADDR" envDefault:"localhost:6379"`
Password string `env:"REDIS_PASS" envDefault:""`
DB int `env:"REDIS_DB" envDefault:"0"`
}
Server struct {
Host string `env:"SERVER_HOST" envDefault:"0.0.0.0"`
Port int `env:"SERVER_PORT" envDefault:"8080"`
ReadTimeout int `env:"SERVER_READ_TIMEOUT" envDefault:"30"`
WriteTimeout int `env:"SERVER_WRITE_TIMEOUT" envDefault:"30"`
CORS []string `env:"SERVER_CORS" envSeparator:"," envDefault:"*"`
}
}
func main() {
// 模拟设置环境变量
os.Setenv("APP_NAME", "AwesomeApp")
os.Setenv("DB_HOST", "postgres.prod")
os.Setenv("DB_PASS", "securepassword123")
os.Setenv("SERVER_PORT", "9090")
os.Setenv("SERVER_CORS", "https://example.com,https://api.example.com")
var cfg Config
if err := spr.Parse(&cfg); err != nil {
log.Fatalf("Failed to parse config: %v", err)
}
fmt.Printf("Application: %s\n", cfg.AppName)
fmt.Printf("Database Connection: postgres://%s@%s:%d/%s\n",
cfg.Database.User, cfg.Database.Host, cfg.Database.Port, cfg.Database.Name)
fmt.Printf("Server listening on: %s:%d\n", cfg.Server.Host, cfg.Server.Port)
fmt.Printf("Allowed CORS origins: %v\n", cfg.Server.CORS)
}
最佳实践
- 集中管理配置:将所有配置集中在一个包中,避免分散在代码各处
- 使用 required 标记:对于必须的配置项,使用
required
标记确保它们被设置 - 提供合理的默认值:为可选的配置项提供合理的默认值
- 环境特定配置:使用
envDefaultProduction
等标记为不同环境提供不同默认值 - 文档化配置:在结构体注释中记录每个配置项的作用和可能的值
Swap/Spr 提供了一种简洁、类型安全的方式来管理应用程序配置,特别适合基于微服务和容器的应用程序,这些应用程序通常依赖环境变量进行配置。