env - 环境变量管理库
env 是一个简单易用的环境变量管理库,提供 .env 文件加载、类型转换、分组查询等功能。采用"一次初始化,多次读取"的设计理念,适用于高并发场景。
安装
go get -u go-slim.dev/env
快速开始
基础用法
package main
import (
"log"
"go-slim.dev/env"
)
func main() {
// 初始化:加载 .env 文件
if err := env.Init(); err != nil {
log.Fatal(err)
}
// 锁定环境变量,防止运行时修改
env.Lock()
// 读取环境变量
port := env.Int("PORT", 8080)
debug := env.Bool("DEBUG", false)
dbHost := env.String("DB_HOST", "localhost")
log.Printf("Server running on port %d", port)
}
.env 文件示例
# 应用配置
APP_ENV=development
APP_NAME=MyApp
PORT=8080
DEBUG=true
# 数据库配置
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mydb
DB_USER=admin
DB_PASSWORD=secret
# 缓存配置
CACHE_DRIVER=redis
CACHE_DATABASE=1
CACHE_SCOPE=app:
核心功能
1. 初始化函数
Init - 初始化环境变量
func Init(root ...string) error
说明:
从运行目录加载 .env 文件并初始化环境变量。这是整个库的入口函数。
参数:
root:可选参数,指定.env文件所在目录,不指定则使用当前程序的运行目录
加载顺序:
- 系统环境变量
.env文件.env.local文件(优先级更高,可覆盖.env).env.{APP_ENV}文件(根据APP_ENV变量).env.{APP_ENV}.local文件(优先级最高)
示例:
// 使用当前目录
if err := env.Init(); err != nil {
log.Fatal(err)
}
// 指定配置目录
if err := env.Init("/path/to/config"); err != nil {
log.Fatal(err)
}
// 多环境配置示例
// .env.development, .env.production 会根据 APP_ENV 自动加载
注意事项:
- 此函数不是线程安全的,必须在程序启动时单线程调用
- 后加载的文件会覆盖先加载的同名变量
- 文件不存在不会报错,只有读取失败才会返回错误
InitWithDir - 从指定目录初始化
func InitWithDir(dir string) error
说明:
从指定的目录加载 .env 文件。与 Init() 的区别是必须提供目录参数。
参数:
dir:.env文件所在目录的绝对路径或相对路径
示例:
// 从指定目录加载
if err := env.InitWithDir("/etc/myapp"); err != nil {
log.Fatal(err)
}
Load - 加载指定文件
func Load(filenames ...string) error
说明:
加载指定的环境变量文件。可用于加载额外的配置文件。
参数:
filenames:要加载的文件路径列表
示例:
// 初始化后加载额外配置
env.Init()
env.Load("/path/to/secrets.env")
env.Load("/path/to/custom1.env", "/path/to/custom2.env")
注意事项:
- 调用
Lock()后无法再调用此函数 - 文件不存在会返回错误
Lock - 锁定环境变量
func Lock()
说明:
锁定全局环境变量,防止后续修改。调用后任何写操作(Init、Load、Updates)都会失败。
最佳实践:
在初始化完成后立即调用 Lock(),确保运行时只能读取环境变量。
示例:
func main() {
// 初始化阶段
if err := env.Init(); err != nil {
log.Fatal(err)
}
// 锁定,防止运行时修改
env.Lock()
// 运行时阶段 - 只能读取
go worker1()
go worker2()
}
特性:
- 锁定是单向的,无法解锁
- 锁定后调用
Load()会返回ErrLocked错误 - 锁定后调用
Updates()或Clean()会导致 panic - 可以重复调用(幂等操作)
2. 查询函数
Lookup - 查询环境变量
func Lookup(name string) (string, bool)
说明:
查询指定的环境变量值。
参数:
name:环境变量名
返回值:
- 第一个返回值:变量的字符串值
- 第二个返回值:
true表示变量存在且值非空,false表示不存在或为空
示例:
if value, exists := env.Lookup("API_KEY"); exists {
fmt.Println("API Key:", value)
} else {
fmt.Println("API Key not set")
}
Exists - 检查变量是否存在
func Exists(name string) bool
说明:
检查环境变量是否存在。与 Lookup 的区别是,只要变量被定义就返回 true,即使值为空。
示例:
if env.Exists("DEBUG") {
// DEBUG 变量已定义(可能为空)
}
// 对比 Lookup
value, exists := env.Lookup("DEBUG")
// exists 只有在 DEBUG 存在且非空时才为 true
String - 获取字符串值
func String(name string, fallback ...string) string
说明:
返回指定环境变量的字符串值。如果不存在或为空,返回默认值。
参数:
name:环境变量名fallback:可选的默认值(可变参数,只使用第一个)
示例:
// 无默认值,不存在时返回空字符串
host := env.String("DB_HOST")
// 有默认值
host := env.String("DB_HOST", "localhost")
name := env.String("APP_NAME", "MyApp")
Int - 获取整数值
func Int(name string, fallback ...int) int
说明:
返回指定环境变量的整数值。如果不存在、为空或转换失败,返回默认值。
示例:
// 无默认值,失败时返回 0
port := env.Int("PORT")
// 有默认值
port := env.Int("PORT", 8080)
maxConn := env.Int("MAX_CONNECTIONS", 100)
Float - 获取浮点数值
func Float(name string, fallback ...float64) float64
说明:
返回指定环境变量的浮点数值。如果不存在、为空或转换失败,返回默认值。
示例:
rate := env.Float("TAX_RATE", 0.08)
ratio := env.Float("COMPRESSION_RATIO", 1.0)
Bool - 获取布尔值
func Bool(name string, fallback ...bool) bool
说明:
返回指定环境变量的布尔值。支持的值:
true: "1", "t", "T", "true", "TRUE", "True"false: "0", "f", "F", "false", "FALSE", "False"
示例:
debug := env.Bool("DEBUG", false)
verbose := env.Bool("VERBOSE", true)
Duration - 获取时间间隔值
func Duration(name string, fallback ...time.Duration) time.Duration
说明:
返回指定环境变量的时间间隔值。支持 Go 标准的时间格式(如 "1h30m")或整数(视为纳秒)。
示例:
// .env 文件
// TIMEOUT=30s
// INTERVAL=5m
timeout := env.Duration("TIMEOUT", 10*time.Second)
interval := env.Duration("INTERVAL", 1*time.Minute)
Bytes - 获取字节切片值
func Bytes(name string, fallback ...[]byte) []byte
说明:
返回指定环境变量的字节切片值。
示例:
data := env.Bytes("BINARY_DATA")
key := env.Bytes("SECRET_KEY", []byte("default-key"))
List - 获取字符串列表
func List(name string, fallback ...[]string) []string
说明:
返回指定环境变量的字符串列表。值使用英文逗号分割,每个元素会自动 trim 空格。
示例:
// .env 文件
// ALLOWED_ORIGINS=http://localhost:3000,https://example.com,https://api.example.com
// TAGS=go, web, api
origins := env.List("ALLOWED_ORIGINS")
// []string{"http://localhost:3000", "https://example.com", "https://api.example.com"}
tags := env.List("TAGS")
// []string{"go", "web", "api"} (自动 trim 空格)
// 使用默认值
hosts := env.List("REDIS_HOSTS", []string{"localhost:6379"})
3. 批量查询函数
Map - 获取相同前缀的所有变量
func Map(prefix string) map[string]string
说明:
将具有相同前缀的环境变量聚合成 map 返回。返回的 key 会去掉前缀部分。
示例:
// .env 文件
// DB_HOST=localhost
// DB_PORT=5432
// DB_NAME=mydb
// DB_USER=admin
dbConfig := env.Map("DB_")
// map[string]string{
// "HOST": "localhost",
// "PORT": "5432",
// "NAME": "mydb",
// "USER": "admin",
// }
Where - 自定义过滤查询
func Where(filter func(name, value string) bool) map[string]string
说明:
返回通过自定义过滤函数筛选后的环境变量。
示例:
// 获取所有以 API_ 开头的变量
apiVars := env.Where(func(name, value string) bool {
return strings.HasPrefix(name, "API_")
})
// 获取所有值为 "true" 的变量
trueVars := env.Where(func(name, value string) bool {
return value == "true"
})
// 获取所有非空变量
nonEmpty := env.Where(func(name, value string) bool {
return value != ""
})
All - 获取所有环境变量
func All() map[string]string
说明:
返回所有环境变量(包括系统环境变量和 .env 文件中的变量)。
示例:
allVars := env.All()
for name, value := range allVars {
fmt.Printf("%s=%s\n", name, value)
}
4. 结构体填充
Fill - 填充结构体
func Fill(structure any) error
说明:
使用环境变量自动填充结构体字段。通过 env 标签指定对应的环境变量名。
示例:
// .env 文件
// APP_NAME=MyApp
// APP_PORT=8080
// APP_DEBUG=true
// DB_HOST=localhost
// DB_PORT=5432
type Config struct {
AppName string `env:"APP_NAME"`
Port int `env:"APP_PORT"`
Debug bool `env:"APP_DEBUG"`
Database struct {
Host string `env:"DB_HOST"`
Port int `env:"DB_PORT"`
}
}
var cfg Config
if err := env.Fill(&cfg); err != nil {
log.Fatal(err)
}
fmt.Println(cfg.AppName) // MyApp
fmt.Println(cfg.Port) // 8080
fmt.Println(cfg.Debug) // true
支持的类型:
- 基本类型:
string,int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,float32,float64,bool - 时间类型:
time.Time,time.Duration - 切片类型:
[]int,[]string等 - 嵌套结构体
注意事项:
- 必须传入结构体指针
- 嵌套结构体会递归填充
- 字段必须是导出的(首字母大写)
- 不支持的类型会返回错误
5. 分组查询 - Signed
Signed - 创建分组查询器
func Signed(prefix, category string) Signer
说明:
创建一个分组查询器,用于操作相同前缀但需要区分不同场景的环境变量。
查询策略:
采用两级查找,带回退机制:
- 首先查找:
PREFIX_CATEGORY_KEY - 如果未找到,回退到:
PREFIX_KEY
这样可以在前缀级别定义通用配置,在分类级别覆盖特定配置。
示例:
// .env 文件
// CACHE_DRIVER=redis
// CACHE_DATABASE=1
// CACHE_SCOPE=app:
// CACHE_BOOK_DATABASE=10
// CACHE_BOOK_SCOPE=app:books:
// CACHE_USER_DATABASE=11
// CACHE_USER_SCOPE=app:users:
// 获取 BOOK 分类的缓存配置
bookCache := env.Signed("CACHE", "BOOK")
bookCache.String("DRIVER") // "redis" (回退到 CACHE_DRIVER)
bookCache.Int("DATABASE") // 10 (来自 CACHE_BOOK_DATABASE)
bookCache.String("SCOPE") // "app:books:" (来自 CACHE_BOOK_SCOPE)
// 获取 USER 分类的缓存配置
userCache := env.Signed("CACHE", "USER")
userCache.String("DRIVER") // "redis" (回退到 CACHE_DRIVER)
userCache.Int("DATABASE") // 11 (来自 CACHE_USER_DATABASE)
userCache.String("SCOPE") // "app:users:" (来自 CACHE_USER_SCOPE)
// 获取默认缓存配置(不指定分类)
defaultCache := env.Signed("CACHE", "")
defaultCache.String("DRIVER") // "redis"
defaultCache.Int("DATABASE") // 1
defaultCache.String("SCOPE") // "app:"
Signer 接口方法:
Signer 接口提供了与全局函数相同的查询方法:
Lookup(key string) (string, bool)Exists(key string) boolString(key string, fallback ...string) stringBytes(key string, fallback ...[]byte) []byteInt(key string, fallback ...int) intDuration(key string, fallback ...time.Duration) time.DurationBool(key string, fallback ...bool) boolList(key string, fallback ...[]string) []stringMap(prefix string) map[string]stringWhere(filter func(name, value string) bool) map[string]stringFill(structure any) error
使用场景:
适用于需要为不同模块、不同环境、不同租户配置相似但有差异的环境变量:
// 多租户配置
tenant1DB := env.Signed("DB", "TENANT1")
tenant2DB := env.Signed("DB", "TENANT2")
// 多服务配置
authService := env.Signed("SERVICE", "AUTH")
apiService := env.Signed("SERVICE", "API")
// 多环境缓存
prodCache := env.Signed("CACHE", "PROD")
devCache := env.Signed("CACHE", "DEV")
6. 辅助函数
Path - 获取相对于初始化目录的路径
func Path(path ...string) string
说明:
返回基于初始化目录的绝对路径。
示例:
// 假设初始化目录为 /app
env.Init("/app")
env.Path() // "/app"
env.Path("config") // "/app/config"
env.Path("logs", "app.log") // "/app/logs/app.log"
Is - 检查应用环境
func Is(env ...string) bool
说明:
检查 APP_ENV 环境变量是否匹配任何给定的值。
示例:
// .env 文件
// APP_ENV=development
if env.Is("development", "dev") {
// 开发环境
log.Println("Running in development mode")
}
if env.Is("production", "prod") {
// 生产环境
} else {
// 非生产环境
}
Updates - 更新环境变量
func Updates(data map[string]string) bool
说明:
批量更新环境变量。返回 true 表示成功,false 表示失败(如已锁定)。
示例:
// 动态设置环境变量
success := env.Updates(map[string]string{
"FEATURE_FLAG_A": "true",
"FEATURE_FLAG_B": "false",
})
if !success {
log.Println("Failed to update environment")
}
注意事项:
- 调用
Lock()后无法使用 - 不是线程安全的,仅在初始化阶段使用
使用场景
1. 多环境配置
// 项目结构
// .env # 默认配置
// .env.local # 本地覆盖(不提交到 git)
// .env.development # 开发环境
// .env.development.local
// .env.production # 生产环境
// .env.production.local
// main.go
func main() {
// 自动根据 APP_ENV 加载对应配置
if err := env.Init(); err != nil {
log.Fatal(err)
}
env.Lock()
// 根据环境执行不同逻辑
if env.Is("development") {
// 开发环境配置
gin.SetMode(gin.DebugMode)
} else if env.Is("production") {
// 生产环境配置
gin.SetMode(gin.ReleaseMode)
}
}
2. 数据库配置管理
type DatabaseConfig struct {
Host string `env:"DB_HOST"`
Port int `env:"DB_PORT"`
Database string `env:"DB_NAME"`
Username string `env:"DB_USER"`
Password string `env:"DB_PASSWORD"`
MaxConns int `env:"DB_MAX_CONNS"`
SSLMode string `env:"DB_SSL_MODE"`
}
func NewDatabaseConfig() (*DatabaseConfig, error) {
cfg := &DatabaseConfig{}
if err := env.Fill(cfg); err != nil {
return nil, err
}
return cfg, nil
}
func main() {
env.Init()
env.Lock()
dbCfg, err := NewDatabaseConfig()
if err != nil {
log.Fatal(err)
}
dsn := fmt.Sprintf("host=%s port=%d dbname=%s user=%s password=%s sslmode=%s",
dbCfg.Host, dbCfg.Port, dbCfg.Database,
dbCfg.Username, dbCfg.Password, dbCfg.SSLMode)
}
3. 多租户/多实例配置
// .env 文件
// REDIS_HOST=localhost
// REDIS_PORT=6379
// REDIS_SESSION_DATABASE=0
// REDIS_SESSION_PREFIX=session:
// REDIS_CACHE_DATABASE=1
// REDIS_CACHE_PREFIX=cache:
// REDIS_QUEUE_DATABASE=2
// REDIS_QUEUE_PREFIX=queue:
// 为不同用途创建不同的 Redis 客户端
func setupRedis() {
env.Init()
env.Lock()
// Session Redis
sessionRedis := env.Signed("REDIS", "SESSION")
sessionClient := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%d",
sessionRedis.String("HOST", "localhost"),
sessionRedis.Int("PORT", 6379)),
DB: sessionRedis.Int("DATABASE", 0),
})
// Cache Redis
cacheRedis := env.Signed("REDIS", "CACHE")
cacheClient := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%d",
cacheRedis.String("HOST", "localhost"),
cacheRedis.Int("PORT", 6379)),
DB: cacheRedis.Int("DATABASE", 1),
})
// Queue Redis
queueRedis := env.Signed("REDIS", "QUEUE")
queueClient := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%d",
queueRedis.String("HOST", "localhost"),
queueRedis.Int("PORT", 6379)),
DB: queueRedis.Int("DATABASE", 2),
})
}
4. 特性开关(Feature Flags)
// .env 文件
// FEATURE_NEW_UI=true
// FEATURE_BETA_API=false
// FEATURE_ANALYTICS=true
type Features struct {
NewUI bool `env:"FEATURE_NEW_UI"`
BetaAPI bool `env:"FEATURE_BETA_API"`
Analytics bool `env:"FEATURE_ANALYTICS"`
}
func main() {
env.Init()
env.Lock()
features := &Features{}
env.Fill(features)
if features.NewUI {
// 启用新 UI
}
if features.BetaAPI {
// 启用 Beta API
}
}
5. 服务发现配置
// .env 文件
// SERVICES=auth,api,notification,payment
// SERVICE_AUTH_URL=http://auth:8001
// SERVICE_API_URL=http://api:8002
// SERVICE_NOTIFICATION_URL=http://notification:8003
// SERVICE_PAYMENT_URL=http://payment:8004
func loadServiceURLs() map[string]string {
env.Init()
env.Lock()
services := env.List("SERVICES")
urls := make(map[string]string)
for _, service := range services {
signer := env.Signed("SERVICE", strings.ToUpper(service))
if url, exists := signer.Lookup("URL"); exists {
urls[service] = url
}
}
return urls
}
// 使用
serviceURLs := loadServiceURLs()
authURL := serviceURLs["auth"] // http://auth:8001
apiURL := serviceURLs["api"] // http://api:8002
线程安全说明
设计模型:"一次初始化,多次读取"
env 库采用了明确的两阶段设计:
初始化阶段(单线程):
- 调用
Init(),InitWithDir(),Load(),Updates() - 必须在启动任何 goroutine 之前完成
- 这些函数不是线程安全的
运行时阶段(多线程):
- 调用
Lookup(),String(),Int()等读取函数 - 所有读取操作都是线程安全的
- 多个 goroutine 可以并发读取
正确用法
func main() {
// ✓ 正确:启动时单线程初始化
if err := env.Init(); err != nil {
log.Fatal(err)
}
// ✓ 最佳实践:锁定防止意外修改
env.Lock()
// ✓ 正确:读取操作线程安全
go func() {
cache := env.Signed("CACHE", "BOOK")
driver := cache.String("DRIVER") // 安全
}()
go func() {
port := env.Int("PORT", 8080) // 安全
}()
}
错误用法
func main() {
// ✗ 错误:并发调用 Init 会导致数据竞争
go env.Init() // 不要这样做!
go env.Init() // 不要这样做!
// ✗ 错误:运行时修改环境变量会导致数据竞争
go func() {
env.Load(".env.runtime") // 不要在运行时这样做!
}()
}
为什么不需要锁?
- 初始化阶段:单线程执行,无需加锁
- 运行时阶段:只读访问,Go 的切片读取本身就是线程安全的
- 环境数据:初始化完成后永不改变
Lock() 函数的作用是在运行时强制执行只读约定,防止意外的写操作。
API 参考
全局函数
| 函数 | 说明 | 线程安全 |
|---|---|---|
Init(root ...string) error | 初始化环境变量 | 否 |
InitWithDir(dir string) error | 从指定目录初始化 | 否 |
Load(filenames ...string) error | 加载指定文件 | 否 |
Lock() | 锁定环境变量 | 是 |
Signed(prefix, category string) Signer | 创建分组查询器 | 是 |
Path(path ...string) string | 获取相对路径 | 是 |
Is(env ...string) bool | 检查应用环境 | 是 |
Updates(data map[string]string) bool | 更新环境变量 | 否 |
查询函数(线程安全)
| 函数 | 说明 |
|---|---|
Lookup(name string) (string, bool) | 查询环境变量 |
Exists(name string) bool | 检查是否存在 |
String(name string, fallback ...string) string | 获取字符串值 |
Bytes(name string, fallback ...[]byte) []byte | 获取字节切片 |
Int(name string, fallback ...int) int | 获取整数值 |
Float(name string, fallback ...float64) float64 | 获取浮点数值 |
Bool(name string, fallback ...bool) bool | 获取布尔值 |
Duration(name string, fallback ...time.Duration) time.Duration | 获取时间间隔 |
List(name string, fallback ...[]string) []string | 获取字符串列表 |
Map(prefix string) map[string]string | 获取相同前缀的所有变量 |
Where(filter func(name, value string) bool) map[string]string | 自定义过滤查询 |
Fill(structure any) error | 填充结构体 |
All() map[string]string | 获取所有环境变量 |
Signer 接口
Signer 提供与全局函数相同的查询方法,但会自动添加前缀和分类。
注意事项
1. 初始化顺序
环境变量的加载顺序会影响最终值(后加载的覆盖先加载的):
系统环境变量
↓
.env
↓ (覆盖)
.env.local
↓ (覆盖)
.env.{APP_ENV}
↓ (覆盖)
.env.{APP_ENV}.local
2. .gitignore 配置
建议的 .gitignore 配置:
# 本地配置不提交
.env.local
.env.*.local
# 可选:也可以不提交 .env
.env
提交 .env 作为模板,使用 .env.local 存储本地敏感配置。
3. 环境变量命名规范
建议的命名规范:
- 使用大写字母和下划线
- 使用前缀分组相关配置(如
DB_,CACHE_,REDIS_) - 使用中间部分表示分类(如
CACHE_BOOK_,CACHE_USER_) - 使用后缀表示具体配置项(如
_HOST,_PORT,_DATABASE)
示例:
# 好的命名
DB_HOST=localhost
DB_PORT=5432
CACHE_REDIS_HOST=localhost
CACHE_REDIS_PORT=6379
# 不推荐
dbhost=localhost # 小写
database-port=5432 # 使用连字符
redis.host=localhost # 使用点号
4. 类型转换失败
当类型转换失败时,会返回默认值:
// .env 文件
// PORT=invalid
port := env.Int("PORT", 8080) // 返回 8080(转换失败,使用默认值)
如果需要知道转换是否成功,使用 Lookup 配合手动转换:
if value, exists := env.Lookup("PORT"); exists {
if port, err := strconv.Atoi(value); err == nil {
// 转换成功
} else {
// 转换失败
log.Printf("Invalid PORT value: %s", value)
}
}
5. 敏感信息保护
不要将敏感信息(密码、密钥等)提交到版本控制系统:
// 使用 .env.local 存储敏感信息
// .env.local(不提交到 git)
DB_PASSWORD=super_secret_password
API_SECRET_KEY=your_secret_key_here
// .env(可以提交,提供模板)
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mydb
DB_USER=admin
# DB_PASSWORD= # 在 .env.local 中设置
6. 默认值的使用
合理使用默认值可以简化配置:
// 开发环境的合理默认值
port := env.Int("PORT", 8080)
debug := env.Bool("DEBUG", true) // 开发环境默认开启
timeout := env.Duration("TIMEOUT", 30*time.Second)
// 这样 .env 文件可以只配置需要修改的值
7. 结构体填充的限制
Fill() 函数的限制:
// ✓ 支持
type Config struct {
Port int `env:"PORT"` // 导出字段 + env 标签
}
// ✗ 不支持
type Config struct {
port int `env:"PORT"` // 未导出字段
Port int // 缺少 env 标签
}
8. Signed 的查找顺序
理解 Signed 的回退机制:
// 环境变量:
// CACHE_DRIVER=redis
// CACHE_BOOK_DATABASE=10
cache := env.Signed("CACHE", "BOOK")
// 查找 DRIVER
// 1. 尝试 CACHE_BOOK_DRIVER (不存在)
// 2. 回退到 CACHE_DRIVER (找到: "redis")
// 查找 DATABASE
// 1. 尝试 CACHE_BOOK_DATABASE (找到: 10)
// 2. 不需要回退
最佳实践
1. 标准初始化流程
func main() {
// 1. 初始化环境变量
if err := env.Init(); err != nil {
log.Fatalf("Failed to initialize env: %v", err)
}
// 2. 锁定,防止运行时修改
env.Lock()
// 3. 加载配置
cfg := loadConfig()
// 4. 启动应用
startApp(cfg)
}
func loadConfig() *Config {
cfg := &Config{}
if err := env.Fill(cfg); err != nil {
log.Fatalf("Failed to load config: %v", err)
}
return cfg
}
2. 配置结构化
// 将相关配置组织到结构体
type AppConfig struct {
Server ServerConfig
Database DatabaseConfig
Redis RedisConfig
Logger LoggerConfig
}
type ServerConfig struct {
Port int `env:"SERVER_PORT"`
Host string `env:"SERVER_HOST"`
Timeout time.Duration `env:"SERVER_TIMEOUT"`
}
type DatabaseConfig struct {
Host string `env:"DB_HOST"`
Port int `env:"DB_PORT"`
Database string `env:"DB_NAME"`
Username string `env:"DB_USER"`
Password string `env:"DB_PASSWORD"`
}
func LoadAppConfig() (*AppConfig, error) {
cfg := &AppConfig{}
if err := env.Fill(cfg); err != nil {
return nil, err
}
return cfg, nil
}
3. 使用 Signed 管理多实例
// 为不同的服务实例使用不同的配置
func setupServices() {
authService := setupService("AUTH")
apiService := setupService("API")
notifyService := setupService("NOTIFICATION")
}
func setupService(name string) *Service {
cfg := env.Signed("SERVICE", name)
return &Service{
URL: cfg.String("URL"),
Timeout: cfg.Duration("TIMEOUT", 30*time.Second),
APIKey: cfg.String("API_KEY"),
}
}
4. 提供配置模板
在项目中提供 .env.example 文件作为配置模板:
# .env.example - 配置模板(提交到 git)
# 应用配置
APP_ENV=development
APP_NAME=MyApp
APP_PORT=8080
APP_DEBUG=true
# 数据库配置(填写实际值)
DB_HOST=localhost
DB_PORT=5432
DB_NAME=
DB_USER=
DB_PASSWORD=
# Redis 配置
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DATABASE=0
用户复制并重命名为 .env,然后填写实际值。
相关链接
- GitHub 仓库
- godotenv - .env 文件解析库
- go-slim.dev/cast - 类型转换库