为什么要
常见格式
一般情况
“YAML 做默认值 + 环境变量做覆盖” :
export APP_PORT=80 即可覆盖文件中的端口,无需改文件package main
import (
"errors"
"fmt"
"log"
"os"
"time"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
package config
type Config struct {
App AppConfig `yaml:"app"`
Database DatabaseConfig `yaml:"database"`
Redis RedisConfig `yaml:"redis"`
Log LogConfig `yaml:"log"`
Email EmailConfig `yaml:"email"`
}
type AppConfig struct {
Name string `yaml:"name"`
Port int `yaml:"port"`
Debug bool `yaml:"debug"`
Version string `yaml:"version"`
Environment string `yaml:"environment"`
}
type DatabaseConfig struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
Name string `yaml:"name"`
}
type RedisConfig struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Password string `yaml:"password"`
DB int `yaml:"db"`
}
type LogConfig struct {
Level string `yaml:"level"`
Path string `yaml:"path"`
MaxSize int `yaml:"max_size"`
MaxBackups int `yaml:"max_backups"`
}
type EmailConfig struct {
SMTPHost string `yaml:"smtp_host"`
SMTPPort int `yaml:"smtp_port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}
var C Config
func Load() error {
v := viper.New()
v.SetConfigName("config")
v.SetConfigType("yaml")
v.AddConfigPath(".")
v.SetEnvPrefix("APP")
v.AutomaticEnv()
// 1. 先尝试本地文件
if _, err := os.Stat("config.yaml"); err == nil {
if err := v.ReadInConfig(); err != nil {
return err
}
return v.Unmarshal(&C)
}
// 2. 本地不存在,用 etcd
if err := v.AddRemoteProvider("etcd", "localhost:2379", "/config/demo"); err != nil {
return err
}
if err := v.ReadRemoteConfig(); err != nil {
return errors.New("local file missing & etcd unreachable")
}
// 反序列化到结构体
if err := v.Unmarshal(&C); err != nil {
return err
}
go func() {
for {
time.Sleep(5 * time.Second)
if err := v.WatchRemoteConfig(); err == nil {
_ = v.Unmarshal(&C)
fmt.Println(">>> etcd config hot-reloaded:", C.App.Name, C.App.Port)
}
}
}()
return nil
}
func main() {
if err := Load(); err != nil {
log.Fatal("load:", err)
}
fmt.Printf("start %s on :%d\n", C.App.Name, C.App.Port)
select {} // 阻塞运行
}