golang实现配置继承与键生成的Viper封装插件piper的使用
golang实现配置继承与键生成的Viper封装插件piper的使用
简介
piper是一个简单的Viper封装插件,主要提供以下特性:
- 单一配置来源:统一管理配置
- 生成键结构体:避免拼写错误
- 配置继承:支持类似Django的配置继承方式
- 多种配置策略支持:灵活适应不同部署场景
- 性能缓存:提高配置读取性能
为什么选择piper
如果你熟悉Django,你会发现Django的配置模块结构如下:
└── settings
├── base.py
├── dev.py
├── stage.py
└── prod.py
dev.py
会继承base.py
,而stage.py
又会继承dev.py
。启动Django时可以选择特定的配置:
export DJANGO_SETTINGS_MODULE=mysite.settings.prod
django-admin runserver
在Django中访问配置的方式:
from django.conf import settings
author = settings.Author
piper提供了类似的体验,但用于Viper:
└── config
├── base.toml
├── dev.toml
├── stage.toml
└── prod.toml
使用piper访问配置的方式:
import "your_project/config"
func main() {
piper.Load("config/stage.toml")
author = piper.GetString(config.Author)
}
安装
go get github.com/Yiling-J/piper/cmd
添加配置文件
将配置文件添加到项目的config文件夹中,通常位于项目根目录下。以下是TOML格式的示例(也支持YAML或JSON):
project
└── config
├── base.toml
├── dev.toml
├── stage.toml
└── prod.toml
要支持配置继承,需要在配置文件中添加一个特殊的键pp_imports
:
pp_imports = ["base.toml", "dev.toml"]
piper会自动解析这个键。顺序很重要,这行表示先导入base.toml
,然后导入dev.toml
并合并。在所有pp_imports
合并后,再导入当前文件。
配置键生成
从项目根目录运行以下命令生成代码:
go run github.com/Yiling-J/piper/cmd your_config_folder
在这个步骤中,piper会加载config文件夹中的所有文件并合并它们,然后在config文件夹下生成config.go
文件,包含所有的配置键。同时你还会看到piper生成代码时显示的配置结构。
生成代码后,config文件夹应该如下所示:
└── config
├── base.toml
├── dev.toml
├── stage.toml
├── prod.toml
└── config.go
使用piper
策略一 - 嵌入
将config文件夹嵌入到代码中,部署时生成单一可执行文件。
import (
"github.com/Yiling-J/piper"
"your_project/example/config"
)
//go:embed config/*
var configFS embed.FS
piper.SetFS(configFS)
piper.Load("config/stage.toml")
author := piper.GetString(config.Author)
策略二 - 嵌入并替换环境变量
将config文件夹嵌入到代码中,部署时生成单一可执行文件,并用环境变量替换敏感信息。
import (
"github.com/Yiling-J/piper"
"your_project/example/config"
)
//go:embed config/*
var configFS embed.FS
os.Setenv("SECRET", "qux")
piper.SetFS(configFS)
// 确保先开启AutomaticEnv再加载配置
// 这样环境变量也会被缓存,IGet*方法才能正常工作
piper.V().AutomaticEnv()
piper.Load("config/stage.toml")
secret := piper.GetString(config.Secret)
策略三 - 复制config目录
构建Docker镜像时复制config文件夹,使真实的config文件夹存在。
import (
"github.com/Yiling-J/piper"
"your_project/example/config"
)
piper.Load("config/stage.toml")
author := piper.GetString(config.Author)
策略四 - 混合嵌入和复制目录
嵌入config文件夹,但将一些敏感密钥保存在真实的配置文件中。
import (
"github.com/Yiling-J/piper"
"your_project/example/config"
)
//go:embed config/*
var configFS embed.FS
// "config/stage_with_secret.toml"不在源代码中
// 可能来自docker构建或k8s ConfigMap
piper.SetFS(configFS)
piper.Load("config/stage_with_secret.toml")
author := piper.GetString(config.Author)
真实目录优先于嵌入目录。
访问Viper
piper只是一个包装器,你可以随时获取包装的viper实例:
v := piper.V()
直接使用viper加载配置时要小心,piper可能无法正常工作。
单一piper还是多个piper?
piper开箱即用,不需要任何配置或初始化。由于大多数应用程序希望使用单一中心存储库来管理配置,piper包提供了这一功能。它类似于单例模式。
上面的所有示例都展示了使用piper的单例风格方法。
使用多个piper
你也可以为应用程序创建许多不同的piper实例。每个实例都有自己独特的配置和值集。每个实例可以从不同的配置文件读取。piper包支持的所有函数都作为piper实例的方法提供。
示例:
x := piper.New()
y := piper.New()
x.Load("config/x.toml")
y.Load("config/y.toml")
// 访问viper
// p := x.V
当使用多个piper时,需要用户自己跟踪不同的piper实例。
性能
有时你可能会发现viper有点慢,因为viper在Get
时需要按以下顺序检查:flag、env、配置文件、键/值存储。如果你确定某些配置不会更改,可以使用piper的IGet*
方法。piper会在Load
时构建配置缓存,这些IGet*
方法会直接从缓存中获取配置。
性能对比:
goos: darwin
goarch: amd64
pkg: github.com/Yiling-J/piper/integration
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkGet-12 895533 1278 ns/op
BenchmarkIGet-12 19141876 61.35 ns/op
更多关于golang实现配置继承与键生成的Viper封装插件piper的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang实现配置继承与键生成的Viper封装插件piper的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang实现配置继承与键生成的Viper封装插件Piper
Piper简介
Piper是一个基于Viper的配置管理封装插件,提供了配置继承和动态键生成功能,使得配置管理更加灵活和强大。下面我将介绍如何使用Piper以及其核心功能实现。
安装Piper
go get github.com/spf13/viper
go get github.com/bingoohuang/piper
基本使用示例
package main
import (
"fmt"
"github.com/bingoohuang/piper"
)
func main() {
// 初始化Piper
p := piper.New("app") // app是配置文件名前缀
// 设置默认值
p.SetDefault("server.port", 8080)
p.SetDefault("database.url", "localhost:3306")
// 读取配置文件
if err := p.ReadConfig(); err != nil {
panic(fmt.Errorf("读取配置失败: %v", err))
}
// 获取配置值
port := p.GetInt("server.port")
fmt.Printf("Server port: %d\n", port)
}
配置继承功能
Piper支持配置继承,可以按照优先级从多个来源加载配置:
- 默认值
- 配置文件
- 环境变量
- 命令行参数
// 继承示例
p := piper.New("app").
WithConfigType("yaml").
WithConfigPaths("./configs", "/etc/app").
WithEnvPrefix("APP").
WithAutomaticEnv()
// 加载顺序:
// 1. 默认值
// 2. ./configs/app.yaml
// 3. /etc/app/app.yaml
// 4. 环境变量 APP_*
// 5. 命令行参数
动态键生成
Piper提供了动态键生成功能,可以根据模式生成配置键:
// 动态键生成示例
p.SetDefault("servers.default.port", 8080)
// 获取配置
port := p.GetInt("servers.{{.env}}.port", piper.WithVars(map[string]interface{}{
"env": "production",
}))
fmt.Printf("Production port: %d\n", port)
完整示例
package main
import (
"fmt"
"log"
"os"
"github.com/bingoohuang/piper"
)
func main() {
// 模拟环境变量
os.Setenv("APP_SERVER_PORT", "9090")
os.Setenv("APP_ENV", "production")
// 初始化Piper
p := piper.New("app").
WithConfigType("yaml").
WithConfigPaths(".").
WithEnvPrefix("APP").
WithAutomaticEnv()
// 设置默认值
p.SetDefault("server.port", 8080)
p.SetDefault("database.url", "localhost:3306")
p.SetDefault("servers.development.port", 8081)
p.SetDefault("servers.production.port", 8082)
// 读取配置文件
if err := p.ReadConfig(); err != nil {
// 配置文件不存在不是错误
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
log.Fatalf("读取配置失败: %v", err)
}
}
// 获取普通配置
port := p.GetInt("server.port") // 从环境变量获取9090
fmt.Printf("Server port: %d\n", port)
// 获取动态配置
env := p.GetString("env") // 从环境变量获取production
dynamicPort := p.GetInt("servers.{{.env}}.port", piper.WithVars(map[string]interface{}{
"env": env,
}))
fmt.Printf("Dynamic port for %s: %d\n", env, dynamicPort)
// 获取所有设置
all := p.AllSettings()
fmt.Printf("All settings: %+v\n", all)
}
高级功能
配置监听与热更新
// 监听配置文件变化
p.WatchConfig()
p.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("配置已更新:", e.Name)
// 重新加载配置逻辑
})
配置验证
// 定义配置结构体
type Config struct {
Server struct {
Port int `mapstructure:"port"`
} `mapstructure:"server"`
}
// 解析到结构体并验证
var cfg Config
if err := p.Unmarshal(&cfg); err != nil {
log.Fatalf("解析配置失败: %v", err)
}
if cfg.Server.Port == 0 {
log.Fatal("server.port 必须配置")
}
多环境配置支持
// 根据环境加载不同配置
env := os.Getenv("APP_ENV")
if env == "" {
env = "development"
}
p := piper.New("app").
WithConfigName(fmt.Sprintf("app.%s", env)).
WithConfigPaths("./configs")
总结
Piper作为Viper的封装插件,提供了以下优势:
- 简化的API接口
- 强大的配置继承机制
- 动态键生成功能
- 多环境支持
- 配置热更新
通过合理使用Piper,可以大大简化Golang项目的配置管理,特别是在微服务架构和云原生应用中,能够提供更加灵活和强大的配置管理能力。
注意:Piper是第三方库,实际使用时请参考其最新文档和版本,上述示例基于常见用法编写,可能需要根据实际库版本进行调整。