配置

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:}

与文件的几个关键差别:

相关示例