一、场景一:小项目 / 微服务(轻量)
go get github.com/joho/godotenv
2. env配置文件
SERVER_HOST=127.0.0.1
SERVER_PORT=8081
DB_USER=root
DB_PASS=123456
3. 代码读取
package main
import (
"fmt"
"log"
"os"
"strconv"
"github.com/joho/godotenv"
)
func main() {
// 加载.env到进程环境变量
err := godotenv.Load()
if err != nil {
log.Println("无.env文件,使用系统环境变量")
}
// 直接用os.Getenv读取字符串
host := os.Getenv("SERVER_HOST")
pass := os.Getenv("DB_PASS")
// 数字需要手动转换
portStr := os.Getenv("SERVER_PORT")
port, err := strconv.Atoi(portStr)
if err != nil {
port = 8080 // 默认值
}
fmt.Println("host:", host)
fmt.Println("port:", port)
fmt.Println("db密码:", pass)
}
4. 特点:
二、场景二:Viper读取yaml配置(中大型项目)
go get github.com/spf13/viper
server:
host: 127.0.0.1
port: 8080
mode: dev
db:
dsn: root:123456@tcp(127.0.0.1:3306)/go_mysql_study?charset=utf8mb4&parseTime=True&loc=Local
max_open: 20
jwt:
secret: "S4PEa1-oSIxm1Uo9Csa7hdSHQ70AzAwQ-yv7A7IqIEY="
expire: 1h
refresh_expire: 7h
3. 代码读取
package main
import (
"fmt"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"log"
)
type AppCfg struct {
Server ServerCfg `mapstructure:"server"`
Db DbCfg `mapstructure:"db"`
JwtCfg JwtCfg `mapstructure:"jwt"`
}
type ServerCfg struct {
Host string `mapstructure:"host"`
Port string `mapstructure:"port"`
Mode string `mapstructure:"mode"`
}
type DbCfg struct {
Dsn string `mapstructure:"dsn"`
MaxOpenConns int `mapstructure:"max_open_conns"`
MaxIdleConns int `mapstructure:"max_idle_conns"`
}
type JwtCfg struct {
Secret string `mapstructure:"secret"`
Expire string `mapstructure:"expire"`
RefreshExpire string `mapstructure:"refresh_expire"`
}
func main() {
v := viper.New()
// 配置文件基础信息
v.SetConfigName("config")
v.SetConfigType("yaml")
// 多路径查找:当前目录、etc目录
v.AddConfigPath(".")
v.AddConfigPath("/etc/app/")
// 读取配置文件
if err := v.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
log.Println("配置文件不存在,仅使用环境变量+默认值")
} else {
log.Fatalf("读取配置失败:%v", err)
}
}
var cfg AppCfg
// 配置热重载:修改yaml自动刷新结构体
v.WatchConfig()
v.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("配置文件变更:", e.Name)
_ = v.Unmarshal(&cfg) // 重新赋值全局Cfg
})
// 映射到结构体(核心)
if err := v.Unmarshal(&cfg); err != nil {
log.Fatalf("结构体绑定失败:%v", err)
}
fmt.Printf("%+v\n", cfg)
fmt.Printf("%+v\n", cfg.Server.Host)
fmt.Printf("%+v\n", cfg.JwtCfg.Secret)
}
三、场景三:Viper + godotenv 读取 .env 配置文件内容(中大型项目)
1. 安装依赖
go get github.com/joho/godotenv
go get github.com/spf13/viper
2. .env 配置文件
# 服务配置
SERVER_HOST=127.0.0.1
SERVER_PORT=8081
SERVER_APP_ENV=dev
# Jwt配置
JWT_SECRET=S4PEa1-oSIxm1Uo9Csa7hdSHQ70AzAwQ-yv7A7IqIEY=
JWT_EXPIRE=12h
3. 读取配置
package main
import (
"fmt"
"github.com/joho/godotenv"
"github.com/spf13/viper"
"log"
"strings"
)
type AppCfg struct {
Server ServerCfg `mapstructure:"server"`
Jwt JwtCfg `mapstructure:"jwt"`
}
type ServerCfg struct {
Host string `mapstructure:"host"`
Port string `mapstructure:"port"`
AppEnv string `mapstructure:"app_env"`
}
type JwtCfg struct {
Secret string `mapstructure:"secret"`
Expire string `mapstructure:"expire"`
}
func main() {
// 加载.env到进程环境变量
err := godotenv.Load()
if err != nil {
log.Println("警告:未找到.env文件,将读取系统环境变量")
}
// 创建viper实例
v := viper.New()
// 开启读取系统环境变量
v.AutomaticEnv()
// 环境变量下划线替换为点,匹配嵌套结构体,server.host → SERVER_HOST
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
// 显式绑定每个环境变量到 viper key
// 注意:仅靠 AutomaticEnv 能让 v.Get() 工作,但 Unmarshal 无法正确读取,
// 必须用 BindEnv 显式注册映射关系
v.BindEnv("server.host", "SERVER_HOST")
v.BindEnv("server.port", "SERVER_PORT")
v.BindEnv("server.app_env", "SERVER_APP_ENV")
v.BindEnv("jwt.secret", "JWT_SECRET")
v.BindEnv("jwt.expire", "JWT_EXPIRE")
// 4. 绑定到全局结构体变量 Cfg
var cfg AppCfg
err = v.Unmarshal(&cfg)
if err != nil {
log.Fatalf("配置绑定结构体失败: %v", err)
}
fmt.Printf("%+v\n", cfg)
}
四、场景四:Viper + godotenv做ymal和env双配置
├── config.yaml # 提交到git,默认配置模板
├── config.yaml.example # 提交到git,带注释说明
├── .env # 不提交,环境差异+密钥
└── .env.example # 提交到git,给开发者参考
**1. 加载优先级(后加载的覆盖前者): **
2. 用 Viper 实现这个优先级链
v.SetConfigName("config") // 读 config.yaml
v.SetConfigType("yaml")
v.AddConfigPath(".")
v.ReadInConfig()
godotenv.Load() // 读 .env(覆盖 yaml 的值)
v.AutomaticEnv() // 系统环境变量(覆盖前两者)
五、大型项目(配置中心方案)
本文为宁采臣原创文章,转载无需和我联系,但请注明来自宁采臣博客http://baijunyao.com
最新评论