跳到主要内容

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 文件所在目录,不指定则使用当前程序的运行目录

加载顺序

  1. 系统环境变量
  2. .env 文件
  3. .env.local 文件(优先级更高,可覆盖 .env
  4. .env.{APP_ENV} 文件(根据 APP_ENV 变量)
  5. .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()

说明

锁定全局环境变量,防止后续修改。调用后任何写操作(InitLoadUpdates)都会失败。

最佳实践

在初始化完成后立即调用 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

说明

创建一个分组查询器,用于操作相同前缀但需要区分不同场景的环境变量。

查询策略

采用两级查找,带回退机制:

  1. 首先查找:PREFIX_CATEGORY_KEY
  2. 如果未找到,回退到: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) bool
  • String(key string, fallback ...string) string
  • Bytes(key string, fallback ...[]byte) []byte
  • Int(key string, fallback ...int) int
  • Duration(key string, fallback ...time.Duration) time.Duration
  • Bool(key string, fallback ...bool) bool
  • List(key string, fallback ...[]string) []string
  • Map(prefix string) map[string]string
  • Where(filter func(name, value string) bool) map[string]string
  • Fill(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,然后填写实际值。


相关链接