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)

结构体字段可以通过以下方式自动"创建"或"配置":

  1. 实现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
}
  1. 实现swap.Configurable接口:
// Configurable接口允许自动初始化为零值的字段的配置
type Configurable interface {
    Configure(configFiles ...string) error
}

// 例如:
func (t *Tool) Configure(configFiles ...string) (err error) {
    return swap.Parse(t, configFiles...)
}
  1. 为该特定字段类型注册了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

1 回复

更多关于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)
}

最佳实践

  1. 集中管理配置:将所有配置集中在一个包中,避免分散在代码各处
  2. 使用 required 标记:对于必须的配置项,使用 required 标记确保它们被设置
  3. 提供合理的默认值:为可选的配置项提供合理的默认值
  4. 环境特定配置:使用 envDefaultProduction 等标记为不同环境提供不同默认值
  5. 文档化配置:在结构体注释中记录每个配置项的作用和可能的值

Swap/Spr 提供了一种简洁、类型安全的方式来管理应用程序配置,特别适合基于微服务和容器的应用程序,这些应用程序通常依赖环境变量进行配置。

回到顶部