package config import ( "encoding/json" "log/slog" "os" "path/filepath" "sync" "time" "github.com/fsnotify/fsnotify" ) // HotReloadConfig 热更新配置 type HotReloadConfig struct { // 配置文件路径 ConfigPath string // 配置更新回调函数 OnUpdate func(*Config) // 配置锁 mu sync.RWMutex // 当前配置 current *Config // 文件监视器 watcher *fsnotify.Watcher // 停止信号 stopChan chan struct{} } // NewHotReloadConfig 创建热更新配置 func NewHotReloadConfig(configPath string, onUpdate func(*Config)) (*HotReloadConfig, error) { watcher, err := fsnotify.NewWatcher() if err != nil { return nil, err } hrc := &HotReloadConfig{ ConfigPath: configPath, OnUpdate: onUpdate, watcher: watcher, stopChan: make(chan struct{}), } // 加载初始配置 if err := hrc.loadConfig(); err != nil { watcher.Close() return nil, err } // 启动文件监视 go hrc.watch() return hrc, nil } // loadConfig 加载配置 func (hrc *HotReloadConfig) loadConfig() error { data, err := os.ReadFile(hrc.ConfigPath) if err != nil { return err } var config Config if err := json.Unmarshal(data, &config); err != nil { return err } hrc.mu.Lock() hrc.current = &config hrc.mu.Unlock() if hrc.OnUpdate != nil { hrc.OnUpdate(&config) } return nil } // watch 监视配置文件变化 func (hrc *HotReloadConfig) watch() { // 添加配置文件目录到监视 configDir := filepath.Dir(hrc.ConfigPath) if err := hrc.watcher.Add(configDir); err != nil { slog.Error("添加配置目录到监视失败", "error", err) return } for { select { case event := <-hrc.watcher.Events: if event.Name == hrc.ConfigPath { // 等待文件写入完成 time.Sleep(100 * time.Millisecond) if err := hrc.loadConfig(); err != nil { slog.Error("重新加载配置失败", "error", err) } } case err := <-hrc.watcher.Errors: slog.Error("配置文件监视错误", "error", err) case <-hrc.stopChan: hrc.watcher.Close() return } } } // Get 获取当前配置 func (hrc *HotReloadConfig) Get() *Config { hrc.mu.RLock() defer hrc.mu.RUnlock() return hrc.current } // Stop 停止热更新 func (hrc *HotReloadConfig) Stop() { close(hrc.stopChan) }