144 lines
3.0 KiB
Go
144 lines
3.0 KiB
Go
|
|
package config
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"sync"
|
||
|
|
|
||
|
|
"github.com/BurntSushi/toml"
|
||
|
|
"github.com/fsnotify/fsnotify"
|
||
|
|
"go.uber.org/zap"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Config 应用配置结构
|
||
|
|
type Config struct {
|
||
|
|
Server ServerConfig `toml:"server"`
|
||
|
|
Log LogConfig `toml:"log"`
|
||
|
|
Protocol ProtocolConfig `toml:"protocol"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// ServerConfig 服务器配置
|
||
|
|
type ServerConfig struct {
|
||
|
|
Host string `toml:"host"`
|
||
|
|
Port int `toml:"port"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// LogConfig 日志配置
|
||
|
|
type LogConfig struct {
|
||
|
|
Level string `toml:"level"`
|
||
|
|
Output string `toml:"output"`
|
||
|
|
MaxSize int `toml:"max_size"`
|
||
|
|
MaxBackups int `toml:"max_backups"`
|
||
|
|
MaxAge int `toml:"max_age"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// ProtocolConfig 协议配置
|
||
|
|
type ProtocolConfig struct {
|
||
|
|
Name string `toml:"name"`
|
||
|
|
Version string `toml:"version"`
|
||
|
|
Options map[string]string `toml:"options"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// ConfigManager 配置管理器
|
||
|
|
type ConfigManager struct {
|
||
|
|
configPath string
|
||
|
|
config *Config
|
||
|
|
logger *zap.Logger
|
||
|
|
mu sync.RWMutex
|
||
|
|
callbacks []func(*Config)
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewConfigManager 创建配置管理器
|
||
|
|
func NewConfigManager(configPath string, logger *zap.Logger) *ConfigManager {
|
||
|
|
return &ConfigManager{
|
||
|
|
configPath: configPath,
|
||
|
|
logger: logger,
|
||
|
|
callbacks: make([]func(*Config), 0),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Load 加载配置文件
|
||
|
|
func (cm *ConfigManager) Load() error {
|
||
|
|
cm.mu.Lock()
|
||
|
|
defer cm.mu.Unlock()
|
||
|
|
|
||
|
|
var cfg Config
|
||
|
|
if _, err := toml.DecodeFile(cm.configPath, &cfg); err != nil {
|
||
|
|
return fmt.Errorf("failed to decode config: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
cm.config = &cfg
|
||
|
|
cm.logger.Info("Config loaded successfully",
|
||
|
|
zap.String("path", cm.configPath),
|
||
|
|
zap.String("server", fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)),
|
||
|
|
)
|
||
|
|
|
||
|
|
// 触发回调
|
||
|
|
for _, cb := range cm.callbacks {
|
||
|
|
cb(cm.config)
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get 获取当前配置
|
||
|
|
func (cm *ConfigManager) Get() *Config {
|
||
|
|
cm.mu.RLock()
|
||
|
|
defer cm.mu.RUnlock()
|
||
|
|
return cm.config
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reload 重新加载配置
|
||
|
|
func (cm *ConfigManager) Reload() error {
|
||
|
|
return cm.Load()
|
||
|
|
}
|
||
|
|
|
||
|
|
// RegisterCallback 注册配置变更回调
|
||
|
|
func (cm *ConfigManager) RegisterCallback(callback func(*Config)) {
|
||
|
|
cm.mu.Lock()
|
||
|
|
defer cm.mu.Unlock()
|
||
|
|
cm.callbacks = append(cm.callbacks, callback)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Watch 监听配置文件变化
|
||
|
|
func (cm *ConfigManager) Watch() error {
|
||
|
|
watcher, err := fsnotify.NewWatcher()
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("failed to create watcher: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := watcher.Add(cm.configPath); err != nil {
|
||
|
|
return fmt.Errorf("failed to watch config file: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
go func() {
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case event, ok := <-watcher.Events:
|
||
|
|
if !ok {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
||
|
|
cm.logger.Info("Config file changed, reloading...",
|
||
|
|
zap.String("file", event.Name))
|
||
|
|
if err := cm.Reload(); err != nil {
|
||
|
|
cm.logger.Error("Failed to reload config",
|
||
|
|
zap.Error(err))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
case err, ok := <-watcher.Errors:
|
||
|
|
if !ok {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
cm.logger.Error("Watcher error", zap.Error(err))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Close 关闭配置管理器
|
||
|
|
func (cm *ConfigManager) Close() error {
|
||
|
|
return nil
|
||
|
|
}
|