Appearance
25|配置管理
go-gateway 运行时需要知道监听端口、数据库地址、Redis 地址、JWT 密钥等参数。这些参数不应该硬编码在代码里,否则每次环境切换(开发 → 测试 → 生产)都要重新编译。配置管理的思路是:把可变参数抽到配置文件或环境变量里,程序启动时读取,运行期间按需要热加载。
一、环境变量
最基础的配置方式是环境变量,适合容器化和云原生部署:
go
package main
import (
"os"
"strconv"
)
type Config struct {
ProxyAddr string
AdminAddr string
DBPath string
RedisAddr string
JWTSecret string
}
func loadFromEnv() Config {
return Config{
ProxyAddr: getEnv("GATEWAY_PROXY_ADDR", ":8080"),
AdminAddr: getEnv("GATEWAY_ADMIN_ADDR", ":8081"),
DBPath: getEnv("GATEWAY_DB_PATH", "./gateway.db"),
RedisAddr: getEnv("GATEWAY_REDIS_ADDR", "127.0.0.1:6379"),
JWTSecret: getEnv("GATEWAY_JWT_SECRET", "change-me"),
}
}
func getEnv(key, fallback string) string {
if val := os.Getenv(key); val != "" {
return val
}
return fallback
}环境变量的优点是简单、不依赖文件、和 Docker/K8s 配合自然。缺点是没有结构、不支持嵌套配置、修改后需要重启进程。
二、配置文件
复杂配置用 YAML 或 JSON 文件管理。Go 生态里 viper 是最流行的配置库,支持多格式、多来源、热加载:
bash
go get github.com/spf13/viperconfig.yaml:
yaml
server:
proxy_addr: ":8080"
admin_addr: ":8081"
database:
driver: sqlite3
dsn: ./gateway.db
redis:
addr: 127.0.0.1:6379
db: 0
jwt:
secret: change-me
expire_hours: 24读取:
go
import "github.com/spf13/viper"
func loadConfig() error {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AddConfigPath("/etc/go-gateway/")
if err := viper.ReadInConfig(); err != nil {
return err
}
proxyAddr := viper.GetString("server.proxy_addr")
jwtSecret := viper.GetString("jwt.secret")
// ...
return nil
}三、多环境配置
开发、测试、生产环境的配置不同,用文件区分:
text
configs/
├── config.yaml # 默认配置
├── config.dev.yaml # 开发环境覆盖
└── config.prod.yaml # 生产环境覆盖通过环境变量选择配置文件:
go
env := getEnv("GATEWAY_ENV", "dev")
viper.SetConfigName("config." + env)
viper.MergeInConfig() // 合并覆盖viper 的读取优先级(高到低):
- 显式
viper.Set的值 - 命令行参数
- 环境变量
- 配置文件
- 默认值
四、配置热加载
网关运行期间修改配置后,不希望重启进程。viper 支持监听配置文件变化:
go
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("config file changed:", e.Name)
// 重新加载路由表、调整连接池等
})热加载不是万能的。监听端口、数据库连接串这类配置变更后,需要显式重建相关资源。配置变更事件里只通知"配置变了",具体的资源重建逻辑由业务代码处理。
五、敏感配置
JWT 密钥、数据库密码不能提交到代码仓库。处理方式:
- 通过环境变量注入
- 用 Docker Secrets / K8s Secrets
- 开发环境用
.env文件(加入.gitignore)
go
viper.BindEnv("jwt.secret", "GATEWAY_JWT_SECRET")六、常见错误
配置和代码不同步
代码里读取 config.GetString("redis.add"),配置文件里写的是 redis.addr——拼写错误导致读到空值,Redis 连接失败。配置字段名变更时,要同步更新所有读取点。
默认值在生产环境生效
go
jwtSecret := getEnv("GATEWAY_JWT_SECRET", "change-me")生产环境忘了设置 GATEWAY_JWT_SECRET,程序用默认的 change-me 启动,安全性形同虚设。启动时检查关键配置是否已设置,未设置则 panic 或退出。
热加载未处理并发
配置变更时,多个 goroutine 可能同时读取和写入配置。用 sync.RWMutex 保护配置对象,或者使用 viper 的原子读取(viper 内部已做并发安全处理)。