配置
YAML 文件 + struct tag。default 给缺省值,env 允许环境变量覆盖,
optional 标记可缺省——三段语义都在 tag 上,不用记魔法字段名。
加载与拒绝
var cfg AppConfig
config.Load("configs/config.yaml", &cfg) // 从文件加载
config.Parse(body, &cfg) // 从 []byte
config.ParseMap(raw, &cfg) // 从 map(热更用它)
if unknown := config.UnknownKeys(raw, &cfg); len(unknown) > 0 {
// 只告警不报错:未知键通常是拼写错误,但直接失败会挡住向前兼容的部署
}
未知键为什么只告警
框架启动时会比对框架段的字段集合,把不认识的键用
obs.Warn 打出来。选择告警而非报错,是因为配置文件可能被更新的版本
写入过——直接拒绝启动会让回滚变危险。
tag 三段语义
type Config struct {
HTTP struct {
Addr string `json:"addr" default:":8080"`
Timeout time.Duration `json:"timeout" default:"10s"`
} `json:"http"`
DB struct {
DSN string `json:"dsn" env:"DB_DSN"` // 环境变量优先于文件
Pool int `json:"pool" default:"16"`
Extra string `json:"extra" optional:""` // 可以缺省
} `json:"db"`
Features []string `json:"features" default:"a,b"` // 逗号分隔
}
| 标记 | 含义 | 说明 |
|---|---|---|
json:"name" |
YAML 键名 | 必写;嵌套结构靠它成段。 |
default:"…" |
缺省值 | 按字段类型解析,time.Duration 支持 "10s" 这类写法。 |
env:"VAR" |
环境变量覆盖 | 优先级最高,容器里改配置的首选。 |
optional:"" |
允许缺省 | 缺了不报错,零值交给业务判断。 |
环境变量展开
除了 tag 级覆盖,YAML 文本本身支持 ${VAR} 与 ${VAR:default},
适合把整段 DSN 拼出来:
db:
driver: postgres
dsn: ${DB_DSN:postgres://matex:matex@localhost:5432/matex?sslmode=disable}
redis:
addr: ${REDIS_ADDR:localhost:6379}
配置文件的结构
框架段 + 每服务一段。服务段的 key 必须等于 Service.Name()——
这就是为什么 Name() 不是装饰。
http: # 框架段:配了就建 HTTP server
addr: ":8080"
timeout: 10s
max_body: 33554432
log:
level: info
db: # 可选:不写这段就没有数据库
driver: postgres
dsn: ${DB_DSN:...}
max_open_conns: 16
slow_threshold: 200ms
grpc: # 可选:还要实现 GRPCRegistrar 才会监听
addr: ":9090"
dial_timeout: 3s
health: true # 内置 grpc.health.v1,默认开
reflection: false # 默认关:它会公开服务面
feature: # 功能开关的静态部分
new-checkout: false
demo: # 服务段,key == DemoService.Name()
greeting: "hello"
consume: false
哪些段是必须的
严格来说只有 http 是值得配的(不配也能起,只是没有 HTTP 端口)。
其余全部可选,且"没配 = 不存在":
# 只有 http + log + 服务段,零外部依赖
$ cat configs/config.min.yaml
# 加一段就多一个能力
$ grep -c '^db:' configs/config.sqlite.yaml
环境变量不能凭空启用一段配置
给
DB_DSN 但配置文件里没有 db: 段,服务不会连数据库。
段的存在是开关,环境变量只覆盖段内的值。
热更
框架用 fsnotify 监听配置文件(100ms 去抖),变化时把服务段交给实现了
ConfigWatcher 的服务。返回什么、怎么并发安全由服务决定:
见 服务与生命周期。
配置中心(Nacos)
本地文件是底座,Nacos 是覆盖层。配了 nacos: 段(或传了
verticle.WithConfig),verticle.Run 在读完文件后拉取两个
data_id——框架段(http/log/db/redis…)
和服务段,逐键合并,再走既有的 ConfigWatcher 热更。
nacos:
server_addr: ${NACOS_ADDR:localhost:8848} # 逗号分隔,可带 https://
namespace: demo # namespace id,不是显示名
group: DEMO
data_id: matex.yaml # 框架段
service_data_id: demo.yaml # 服务段;空 = 用文件里的服务段
timeout: 10s # 首次拉取预算,失败即退出
username: ${NACOS_USERNAME:nacos} # 二选一:账号密码…
password: ${NACOS_PASSWORD:nacos}
# access_key: ${NACOS_ACCESS_KEY:} # …或 RAM AK/SK(STS 再加 sts_token)
# secret_key: ${NACOS_SECRET_KEY:}
与文件的几个关键差别:
- 服务段实时生效:变化走
ConfigWatcher,和改文件一样;框架段是 启动时建好的(连接池、监听、tracing),变化只告警,等重启或滚动发布。 - data_id 不存在 = 启动失败:Nacos 对不存在的 key 回答空内容,不把它当成空配置。
- 首次拉取必须成功:不会用本地快照顶着跑。
- 远端是平铺 map:重载前用
config.UnknownKeys拒绝未知键,否则拼错 的键会解码成零值。
相关示例
- examples/config:tag 语义、
${VAR}、环境覆盖。 - examples/nacos:把框架段与服务段放进 Nacos,服务段实时热更。
- examples/lifecycle:热更回调与拒绝非法新配置。