Go初始化配置文件

一、场景一:小项目 / 微服务(轻量)

  • 使用godotenv + os.Getenv,配置.env文件,不引入 Viper。
    1. 安装依赖
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. 特点:

  • godotenv.Load() 仅把 .env 键值注入当前进程环境,不修改系统;
  • os.Getenv("KEY") 只能返回字符串,数字 / 布尔要手动转换;
  • 没有结构体自动绑定,配置多了代码冗余。

二、场景二:Viper读取yaml配置(中大型项目)

  • Viper可以读取到yaml配置,解析到结构体使用。
  • Viper可以配置热加载,修改yaml文件自动解析到结构体。
  1. 安装依赖
go get github.com/spf13/viper
  1. 配置config.yaml文件
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 配置文件内容(中大型项目)

  • Viper推荐配置yaml、omal、env等推荐的写法是 server.host,这样方便解析所有配置到机构体。
  • 但是如果.env配置文件写法是大写 + 下划线 SERVER_HOST,就需要显式绑定每个环境变量,否则当结构体配置嵌套时,读取到配置都是空!!!

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双配置

  • ymal配置基本信息提交git,env配置环境差异、密钥、密码等,env不提交git,如下情景结构:
  ├── config.yaml          # 提交到git,默认配置模板                                                                   
  ├── config.yaml.example  # 提交到git,带注释说明                                                                     
  ├── .env                 # 不提交,环境差异+密钥                                                                     
  └── .env.example         # 提交到git,给开发者参考                                                                   

**1. 加载优先级(后加载的覆盖前者): **

  • config.yaml(默认值)
  • .env(开发环境差异)
  • 系统环境变量(生产环境,Docker/K8s 注入)

2. 用 Viper 实现这个优先级链

  v.SetConfigName("config")    // 读 config.yaml                                                                       
  v.SetConfigType("yaml")                                                                                              
  v.AddConfigPath(".")                                                                                                 
  v.ReadInConfig()                                                                                                     
                                                                                                                       
  godotenv.Load()              // 读 .env(覆盖 yaml 的值)                                                            
  v.AutomaticEnv()             // 系统环境变量(覆盖前两者)                                                           

五、大型项目(配置中心方案)

  • yaml / toml 只做本地兜底,实际配置从 配置中心(etcd/consul/apollo/nacos)拉取,.env 仅管理配置中心地址和密钥。

张贤博客
请先登录后发表评论
  • latest comments
  • 总共0条评论