假如我们有以下配置文件( config.yml ):
1
2
3
4
5
6
7
8
9
10
11
12
|
database:
sqlite:
path: ./main.db
mysql:
host: localhost
port: 3306
username: hello
password: god.pwd
database: dev
jwt:
secret: ok-this-is-jst-sec
expire_time: 24
|
使用 yaml 库加载
- 先使用
os.ReadFile 读取文件内容。
- 再使用
yaml.Unmarshal 反序列化文件内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
package main
import (
"fmt"
"log"
"os"
"gopkg.in/yaml.v3"
)
type Config struct {
Database DatabaseConfig `yaml:"database"`
JWT JWTConfig `yaml:"jwt"`
}
type DatabaseConfig struct {
MySQL MySQLConfig `yaml:"mysql"`
Sqlite SqliteConfig `yaml:"sqlite"`
}
type MySQLConfig struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
Database string `yaml:"database"`
}
type SqliteConfig struct {
Path string `yaml:"path"`
}
type JWTConfig struct {
Secret string `yaml:"secret"`
ExpireTime int `yaml:"expire_time"`
}
func main() {
cfg := &Config{}
data, err := os.ReadFile("config.yml")
if err != nil {
log.Fatal(err)
}
err = yaml.Unmarshal(data, cfg)
if err != nil {
log.Fatal(err)
}
fmt.Println(cfg)
fmt.Println(cfg.Database.MySQL.Username)
}
|
输出:
1
2
3
4
|
$ go run .
&{{{localhost 3306 hello god.pwd dev} {./main.db}} {ok-this-is-jst-sec 24}}
hello
$
|
使用 viper 加载
viper 加载与 yaml 加载类似,但支持的功能更多,以热更新为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
package main
import (
"fmt"
"log"
"time"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
type Config struct {
Database DatabaseConfig `yaml:"database"`
JWT JWTConfig `yaml:"jwt"`
}
type DatabaseConfig struct {
MySQL MySQLConfig `yaml:"mysql"`
Sqlite SqliteConfig `yaml:"sqlite"`
}
type MySQLConfig struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
Database string `yaml:"database"`
}
type SqliteConfig struct {
Path string `yaml:"path"`
}
type JWTConfig struct {
Secret string `yaml:"secret"`
ExpireTime int `yaml:"expire_time"`
}
func main() {
// 1. 设置配置文件名称(不带后缀)
viper.SetConfigName("config")
// 2. 设置配置文件类型:yml / yaml
viper.SetConfigType("yml")
// 3. 设置配置文件路径(当前目录)
viper.AddConfigPath(".")
// 读取配置
cfg := Config{}
err := viper.ReadInConfig()
if err != nil {
log.Fatal(err)
}
// 反序列化配置
err = viper.Unmarshal(&cfg)
if err != nil {
log.Fatal(err)
}
fmt.Println(cfg)
fmt.Println(cfg.Database.MySQL.Username)
// 开启配置文件监听热更新
viper.WatchConfig()
// 配置变化回调函数
viper.OnConfigChange(func(e fsnotify.Event) {
log.Println("检测到配置文件修改,重新加载配置...")
// 重新反序列化到全局变量
if err := viper.Unmarshal(&cfg); err != nil {
log.Printf("热更新重新解析配置失败: %v\n", err)
} else {
log.Println("配置热更新成功!")
os.Exit(0)
}
})
// 循环打印,测试是否实时更新
for {
fmt.Println(cfg)
time.Sleep(3 * time.Second)
}
}
|
输出:
1
2
3
4
5
6
7
8
|
$ go run .
{{{localhost 3306 hello god.pwd dev2} {./main.db}} {ok-this-is-jst-sec 0}}
hello
{{{localhost 3306 hello god.pwd dev2} {./main.db}} {ok-this-is-jst-sec 0}}
{{{localhost 3306 hello god.pwd dev2} {./main.db}} {ok-this-is-jst-sec 0}}
2026/05/04 18:23:14 检测到配置文件修改,重新加载配置...
2026/05/04 18:23:14 配置热更新成功!
$
|
viper 还有一种快捷加载方式,无需事先编写结构体。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
// 1. 设置配置文件名称(不带后缀)
viper.SetConfigName("config")
// 2. 设置配置文件类型:yml / yaml
viper.SetConfigType("yml")
// 3. 设置配置文件路径(当前目录)
viper.AddConfigPath(".")
// 4. 读取配置
viper.ReadInConfig()
// 5. 访问具体的配置项
db := viper.Get("database")
host := viper.GetString("database.mysql.host")
port := viper.GetInt("database.mysql.port")
fmt.Println(db, host, port)
}
|
输出:
1
2
3
|
$ go run .
map[mysql:map[database:dev2 host:localhost password:god.pwd port:3306 username:hello] sqlite:map[path:./main.db]] localhost 3306
$
|