config

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 29, 2026 License: MIT Imports: 13 Imported by: 0

README

config

Go Reference

config 是 Genesis 的 L0 配置组件,基于 Viper 提供统一的多源配置加载与文件驱动的变更通知能力。它面向微服务和组件库场景,解决配置文件、环境特定配置、进程环境变量和 .env 文件之间的统一加载问题。

组件定位

config 的职责很明确:

  • 统一加载基础配置文件与环境特定配置文件
  • 统一处理环境变量与 .env 的覆盖顺序
  • 说明 .env 会补写到进程环境变量里,属于有意的进程级副作用
  • 提供 GetUnmarshalUnmarshalKey 等读取能力
  • 提供按 key 订阅的文件变更通知

它当前不负责以下能力:

  • 远程配置中心
  • 运行时环境变量热更新
  • .env 文件监听
  • 复杂 schema 校验框架

配置优先级

当前优先级从高到低为:

  1. 进程环境变量
  2. .env 文件
  3. 环境特定配置文件,例如 config.dev.yaml
  4. 基础配置文件,例如 config.yaml

这里有一个重要约定:.env 的语义是“补齐缺失项”,不会覆盖当前进程里已经存在的同名环境变量。加载 .env 时,组件会通过 os.Setenv 把缺失项补写进当前进程环境,因此它不是纯本地读文件操作,而是有意的进程级副作用。这比让 .env 反向覆盖部署时显式传入的环境变量更常见,也更容易解释最终行为。

快速开始

loader, err := config.New(&config.Config{
    Name:      "config",
    Paths:     []string{"./config"},
    FileType:  "yaml",
    EnvPrefix: "GENESIS",
})
if err != nil {
    return err
}

if err := loader.Load(ctx); err != nil {
    return err
}

var cfg AppConfig
if err := loader.Unmarshal(&cfg); err != nil {
    return err
}

热更新

Load 只负责加载配置,不会自动启动文件监听。第一次调用 Watch 时,组件才会启动内部 watcher,因此推荐的调用顺序是先 Load,再 Watch

if err := loader.Load(ctx); err != nil {
    return err
}

ch, err := loader.Watch(ctx, "mysql.host")
if err != nil {
    return err
}

for event := range ch {
    fmt.Printf("%s: %v -> %v\n", event.Key, event.OldValue, event.Value)
}

热更新当前有明确边界:

  • 只监听基础配置文件和环境特定配置文件
  • 事件来源固定为 file
  • 不监听 .env 文件
  • 不监听运行时环境变量变化
  • 若重载时配置读取、合并或校验失败,不推送变更事件

如果你希望在热更新失败时看到明确告警,可以通过 WithLogger 注入日志器:

logger, _ := clog.New(clog.NewProdDefaultConfig("genesis"))

loader, err := config.New(&config.Config{
    Name:      "config",
    Paths:     []string{"./config"},
    FileType:  "yaml",
    EnvPrefix: "GENESIS",
}, config.WithLogger(logger))

环境特定配置

config/
├── config.yaml
├── config.dev.yaml
└── config.prod.yaml

通过 ${PREFIX}_ENV 选择环境,例如 GENESIS_ENV=dev 会在基础配置之上合并 config.dev.yaml。环境配置是“增量覆盖”,不是完全替换,因此基础配置里的默认值仍然有效。

环境变量映射

配置 key 环境变量
mysql.host GENESIS_MYSQL_HOST
redis.addr GENESIS_REDIS_ADDR
app.debug GENESIS_APP_DEBUG

规则是:将 key 中的 .- 替换为 _,转成大写,再加上前缀。

推荐用法

  • 应用启动时先 Load,确认配置可用后再构造其他组件
  • 业务配置优先使用 UnmarshalUnmarshalKey 映射到结构体,而不是到处手写 Get
  • 只监听真正需要热更新的 key,不要把 Watch 当成全量配置广播
  • 在容器或生产环境里,优先使用显式环境变量覆盖配置文件
  • 需要排查热更新失败时,为 loader 注入 WithLogger

相关文档

Documentation

Overview

Package config 为 Genesis 提供统一的多源配置加载与文件驱动的变更通知能力。

这个组件基于 Viper 实现,但对外收敛成更稳定的 Loader 契约,用来统一处理 配置文件、环境变量、.env 文件和环境特定配置之间的关系。它面向微服务和组件库场景, 重点解决三类问题:

  • 多源配置的统一加载与覆盖顺序
  • config.yaml 与 config.{env}.yaml 的合并
  • 按 key 订阅配置文件变化,而不是让业务代码直接面对 fsnotify

当前优先级从高到低为:

  • 进程环境变量
  • .env 文件
  • 环境特定配置文件,例如 config.dev.yaml
  • 基础配置文件,例如 config.yaml

其中 .env 的语义是“补齐缺失项”:只有当前进程中不存在同名环境变量时,才会从 .env 注入值。这比“无条件覆盖环境变量”更符合常见实践,也更容易解释部署时的最终结果。

热更新当前只覆盖配置文件本身:

  • Load 负责加载配置,不会自动启动 watcher
  • Watch 只能在成功 Load 后调用;第一次调用 Watch 时才会启动内部文件监听
  • 只监听基础配置文件和环境特定配置文件
  • 不监听 .env 文件,也不监听运行时环境变量变化
  • 热更新时如果读取或校验失败,不推送变更事件
  • 如需记录热更新失败原因,可通过 WithLogger 注入日志器

基本使用:

loader, err := config.New(&config.Config{
	Name:      "config",
	Paths:     []string{"./config"},
	FileType:  "yaml",
	EnvPrefix: "GENESIS",
})
if err != nil {
	panic(err)
}

if err := loader.Load(context.Background()); err != nil {
	panic(err)
}

var cfg AppConfig
if err := loader.Unmarshal(&cfg); err != nil {
	panic(err)
}

监听特定 Key 的变化:

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

ch, _ := loader.Watch(ctx, "app.debug")
for event := range ch {
	_ = event
}

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrValidationFailed 配置验证失败
	ErrValidationFailed = xerrors.New("configuration validation failed")

	// ErrNotLoaded 配置尚未加载
	ErrNotLoaded = xerrors.New("configuration not loaded")
)

Functions

This section is empty.

Types

type Config

type Config struct {
	Name      string   // 配置文件名称,不含扩展名;默认 "config"
	Paths     []string // 配置文件搜索路径;默认 [".", "./config"]
	FileType  string   // 配置文件类型,如 yaml、json;默认 "yaml"
	EnvPrefix string   // 环境变量前缀;默认 "GENESIS"
}

Config 定义 Loader 的加载参数。

它只描述“去哪里找配置,以及如何把 key 映射到环境变量”,不承载业务配置本身。

type Event

type Event struct {
	Key       string      // 配置 key
	Value     any         // 新值
	OldValue  any         // 旧值
	Source    EventSource // 事件来源
	Timestamp time.Time
}

Event 配置变更事件

type EventSource added in v0.5.0

type EventSource string

EventSource 表示配置变更事件的来源。

const (
	// EventSourceFile 表示事件来自配置文件变化。
	EventSourceFile EventSource = "file"
)

type Loader

type Loader interface {
	// Load 加载配置并初始化内部状态。
	//
	// Load 可以重复调用。每次调用都会基于当前 Config 重新创建内部 Viper 状态,
	// 并重新读取基础配置、环境配置和环境变量;.env 也会重新处理,但只补齐当前
	// 仍然缺失的环境变量。
	Load(ctx context.Context) error

	// Get 获取原始配置值
	Get(key string) any

	// Unmarshal 将整个配置反序列化到结构体
	Unmarshal(v any) error

	// UnmarshalKey 将指定 Key 的配置反序列化到结构体
	UnmarshalKey(key string, v any) error

	// Watch 监听配置变化,通过 context 取消监听。
	//
	// 实现细节:
	//   - 调用 Watch 前必须先成功执行 Load
	//   - 无论调用多少次 Watch,内部只启动一个文件监听 goroutine(sync.Once 保证)
	//   - 返回的 channel 缓冲区大小为 10,若消费者处理过慢可能丢失事件(非阻塞发送)
	//   - 监听基础配置文件和环境特定配置文件(如 config.yaml 和 config.dev.yaml)
	//   - .env 文件变更不会触发通知
	//   - 热更新时若配置文件读取失败,不会推送变更事件,也不会返回错误
	//   - 该方法的 Load 前置检查用于快速失败,不负责等待并发中的 Load 完成
	Watch(ctx context.Context, key string) (<-chan Event, error)

	// Validate 验证当前配置的有效性
	Validate() error
}

Loader 定义配置加载器的核心行为。 它负责加载、读取、反序列化和监听配置变化。

func New

func New(cfg *Config, opts ...Option) (Loader, error)

New 创建配置加载器。

如果 cfg 为 nil,使用默认配置。 New 会复制一份 Config,调用方后续不应依赖修改原始 cfg 来影响已创建的 Loader。

type Option added in v0.5.0

type Option func(*loader)

Option 定义 Loader 的可选配置。

func WithLogger added in v0.5.0

func WithLogger(logger clog.Logger) Option

WithLogger 为 Loader 注入日志器。

当配置热更新失败时,config 会通过该日志器输出告警,帮助调用方定位读取失败、 合并失败或校验失败等问题。未注入时默认使用 clog.Discard()。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL