全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-18 10 分钟 ✍️ juanwangdev

Gin 多环境配置管理

多环境配置管理是应用部署的基础,确保开发、测试、生产环境配置独立且安全。

环境区分

环境类型

Go
- development:开发环境,详细日志、热重载
- test:测试环境,测试数据库、Mock 服务
- staging:预发布环境,接近生产配置
- production:生产环境,优化配置、安全设置

环境标识

Bash
import "os"

func GetEnv() string {
    env := os.Getenv("APP_ENV")
    if env == "" {
        return "development"
    }
    return env
}

func main() {
    env := GetEnv()

    switch env {
    case "development":
        gin.SetMode(gin.DebugMode)
    case "test":
        gin.SetMode(gin.TestMode)
    case "production":
        gin.SetMode(gin.ReleaseMode)
    }

    r := gin.Default()
    r.Run(getPort(env))
}

func getPort(env string) string {
    switch env {
    case "production":
        return ":80"
    case "test":
        return ":3000"
    default:
        return ":8080"
    }
}

Viper 配置管理

安装 Viper

YAML
go get github.com/spf13/viper

配置文件结构

Go
# config/development.yaml
app:
  name: gin-api
  port: 8080
  mode: debug

database:
  host: localhost
  port: 3306
  user: root
  password: root123
  name: gin_dev

redis:
  host: localhost
  port: 6379
  password: ""
  db: 0

log:
  level: debug
  format: text
  output: stdout

---
# config/production.yaml
app:
  name: gin-api
  port: 80
  mode: release

database:
  host: db.internal.example.com
  port: 3306
  user: gin_user
  password: ${DB_PASSWORD}  # 环境变量
  name: gin_prod

redis:
  host: redis.internal.example.com
  port: 6379
  password: ${REDIS_PASSWORD}
  db: 1

log:
  level: info
  format: json
  output: file
  file: /var/log/gin-api/app.log

Viper 加载配置

Go
import (
    "github.com/spf13/viper"
)

type Config struct {
    App      AppConfig
    Database DatabaseConfig
    Redis    RedisConfig
    Log      LogConfig
}

type AppConfig struct {
    Name string
    Port int
    Mode string
}

type DatabaseConfig struct {
    Host     string
    Port     int
    User     string
    Password string
    Name     string
}

type RedisConfig struct {
    Host     string
    Port     int
    Password string
    DB       int
}

type LogConfig struct {
    Level  string
    Format string
    Output string
    File   string
}

func LoadConfig() (*Config, error) {
    env := GetEnv()

    v := viper.New()

    // 设置配置文件名和路径
    v.SetConfigName(env)
    v.SetConfigType("yaml")
    v.AddConfigPath("config")
    v.AddConfigPath("./config")
    v.AddConfigPath("../config")

    // 支持环境变量覆盖
    v.AutomaticEnv()
    v.SetEnvPrefix("APP")

    // 读取配置
    if err := v.ReadInConfig(); err != nil {
        return nil, err
    }

    // 绑定到结构体
    var config Config
    if err := v.Unmarshal(&config); err != nil {
        return nil, err
    }

    return &config, nil
}

环境变量覆盖

Go
func SetupViper() {
    v := viper.New()

    // 设置默认值
    v.SetDefault("app.port", 8080)
    v.SetDefault("log.level", "info")

    // 配置文件
    v.SetConfigName("config")
    v.SetConfigType("yaml")
    v.AddConfigPath(".")

    // 环境变量绑定
    v.BindEnv("database.password", "DB_PASSWORD")
    v.BindEnv("redis.password", "REDIS_PASSWORD")
    v.BindEnv("app.port", "APP_PORT")

    // 自动环境变量
    v.AutomaticEnv()

    // 替换环境变量占位符
    v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

    v.ReadInConfig()
}

配置目录结构

Go
project/
├── config/
│   ├── development.yaml
│   ├── test.yaml
│   ├── staging.yaml
│   ├── production.yaml
│   └── config.local.yaml  # 本地覆盖(不提交)
├── .env                   # 环境变量文件(不提交)
├── .env.example           # 环境变量模板
└── main.go

配置验证

dockerfile
func ValidateConfig(config *Config) error {
    if config.App.Port < 1 || config.App.Port > 65535 {
        return errors.New("invalid port")
    }

    if config.Database.Host == "" {
        return errors.New("database host required")
    }

    if config.Database.Name == "" {
        return errors.New("database name required")
    }

    if config.App.Mode != "debug" && config.App.Mode != "release" {
        return errors.New("invalid app mode")
    }

    return nil
}

func main() {
    config, err := LoadConfig()
    if err != nil {
        log.Fatal("Load config failed:", err)
    }

    if err := ValidateConfig(config); err != nil {
        log.Fatal("Config validation failed:", err)
    }
}

配置热更新

YAML
func WatchConfig(config *Config, callback func(*Config)) {
    v := viper.GetViper()

    v.OnConfigChange(func(e fsnotify.Event) {
        log.Println("Config file changed:", e.Name)

        var newConfig Config
        if err := v.Unmarshal(&newConfig); err != nil {
            log.Println("Config parse error:", err)
            return
        }

        callback(&newConfig)
    })

    v.WatchConfig()
}

func main() {
    config, _ := LoadConfig()

    WatchConfig(config, func(newConfig *Config) {
        // 更新全局配置
        *config = *newConfig

        // 动态调整日志级别
        updateLogLevel(config.Log.Level)

        log.Println("Config updated")
    })
}

Docker 环境配置

Bash
# Dockerfile
FROM golang:1.21-alpine

ENV APP_ENV=production
ENV APP_PORT=80

WORKDIR /app
COPY . .
RUN go build -o gin-api .

CMD ["./gin-api"]
Go
# docker-compose.yml
version: '3'
services:
  api:
    build: .
    environment:
      - APP_ENV=production
      - DB_PASSWORD=${DB_PASSWORD}
      - REDIS_PASSWORD=${REDIS_PASSWORD}
    ports:
      - "80:80"
    depends_on:
      - db
      - redis

  db:
    image: mysql:8
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_PASSWORD}

  redis:
    image: redis:alpine
    command: redis-server --requirepass ${REDIS_PASSWORD}

环境变量文件

Go
# .env.example(提交到仓库)
APP_ENV=development
APP_PORT=8080
DB_PASSWORD=
REDIS_PASSWORD=

# .env(本地,不提交)
APP_ENV=development
APP_PORT=8080
DB_PASSWORD=local_password
REDIS_PASSWORD=redis_pass
text
// 加载 .env 文件
import "github.com/joho/godotenv"

func LoadEnvFile() {
    env := GetEnv()
    envFile := ".env." + env

    // 优先加载特定环境文件
    if err := godotenv.Load(envFile); err != nil {
        // 回退到默认文件
        godotenv.Load(".env")
    }
}

配置管理器

text
type ConfigManager struct {
    config    *Config
    v         *viper.Viper
    callbacks []func(*Config)
}

func NewConfigManager() *ConfigManager {
    cm := &ConfigManager{
        v:         viper.New(),
        callbacks: make([]func(*Config), 0),
    }

    cm.loadConfig()
    cm.watchConfig()

    return cm
}

func (cm *ConfigManager) Get() *Config {
    return cm.config
}

func (cm *ConfigManager) OnChange(callback func(*Config)) {
    cm.callbacks = append(cm.callbacks, callback)
}

func (cm *ConfigManager) loadConfig() {
    env := GetEnv()
    cm.v.SetConfigName(env)
    cm.v.SetConfigType("yaml")
    cm.v.AddConfigPath("config")
    cm.v.AutomaticEnv()

    cm.v.ReadInConfig()
    cm.v.Unmarshal(&cm.config)
}

func (cm *ConfigManager) watchConfig() {
    cm.v.WatchConfig()
    cm.v.OnConfigChange(func(e fsnotify.Event) {
        cm.v.Unmarshal(&cm.config)
        for _, cb := range cm.callbacks {
            cb(cm.config)
        }
    })
}

环境配置对比

配置项developmentproduction
Gin Modedebugrelease
Log Leveldebuginfo/warn
Log Formattextjson
DB Hostlocalhost内网地址
Password明文环境变量

注意:敏感信息(密码、密钥)禁止写入配置文件,使用环境变量。

要点总结

  1. 环境区分:通过 APP_ENV 环境变量标识
  2. Viper:支持 YAML、JSON、TOML 多格式配置
  3. 环境变量:敏感信息通过环境变量注入
  4. 配置验证:加载后验证必要字段
  5. 热更新v.WatchConfig() 监听配置变化
  6. 目录结构:config/ 目录存放各环境配置

📝 发现内容有误?点击此处直接编辑

← 上一篇 Gin 中间件与配置集成
下一篇 → Gin 框架热重载配置
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库