mosdns/coremain/hot_reload.go

161 lines
4.5 KiB
Go

/*
* Copyright (C) 2020-2022, IrineSistiana
*
* This file is part of mosdns.
*
* mosdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* mosdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package coremain
import (
"fmt"
"io"
"sync"
"go.uber.org/zap"
)
// HotReloadManager 热加载管理器
type HotReloadManager struct {
mosdns *Mosdns
mu sync.RWMutex
isReloading bool
configPath string
}
// NewHotReloadManager 创建热加载管理器
func NewHotReloadManager(m *Mosdns, configPath string) *HotReloadManager {
return &HotReloadManager{
mosdns: m,
configPath: configPath,
}
}
// Reload 执行热加载
// 返回值:成功加载的插件数,错误信息
func (hrm *HotReloadManager) Reload() (int, error) {
hrm.mu.Lock()
if hrm.isReloading {
hrm.mu.Unlock()
return 0, fmt.Errorf("reload is already in progress")
}
hrm.isReloading = true
hrm.mu.Unlock()
defer func() {
hrm.mu.Lock()
hrm.isReloading = false
hrm.mu.Unlock()
}()
hrm.mosdns.logger.Info("🔄 开始热加载配置...")
// 1. 加载新配置
newCfg, actualPath, err := loadConfig(hrm.configPath)
if err != nil {
hrm.mosdns.logger.Error("failed to load config", zap.Error(err))
return 0, fmt.Errorf("配置文件加载失败: %w", err)
}
hrm.mosdns.logger.Info("✅ 配置文件加载成功", zap.String("path", actualPath))
// 2. 验证新配置
validator := NewConfigValidator(newCfg, hrm.mosdns.logger)
if err := validator.Validate(); err != nil {
hrm.mosdns.logger.Error("config validation failed", zap.Error(err))
return 0, fmt.Errorf("配置验证失败: %w", err)
}
hrm.mosdns.logger.Info("✅ 配置验证通过")
// 3. 备份旧插件
oldPlugins := hrm.mosdns.plugins
hrm.mosdns.logger.Info("📦 备份旧插件", zap.Int("count", len(oldPlugins)))
// 4. 创建新插件映射
hrm.mosdns.plugins = make(map[string]any)
// 5. 加载预设插件
if err := hrm.mosdns.loadPresetPlugins(); err != nil {
// 恢复旧插件
hrm.mosdns.plugins = oldPlugins
hrm.mosdns.logger.Error("failed to load preset plugins", zap.Error(err))
return 0, fmt.Errorf("预设插件加载失败: %w", err)
}
// 6. 加载新配置的插件
if err := hrm.mosdns.loadPluginsFromCfg(newCfg, 0); err != nil {
// 恢复旧插件
hrm.mosdns.plugins = oldPlugins
hrm.mosdns.logger.Error("failed to load plugins from new config", zap.Error(err))
return 0, fmt.Errorf("新插件加载失败: %w", err)
}
newPluginCount := len(hrm.mosdns.plugins)
hrm.mosdns.logger.Info("✅ 新插件加载成功", zap.Int("count", newPluginCount))
// 7. 关闭旧插件(在新插件成功加载后)
hrm.closeOldPlugins(oldPlugins)
// 8. 更新配置引用
hrm.mosdns.config = newCfg
hrm.mosdns.logger.Info("🎉 热加载完成!",
zap.Int("old_plugin_count", len(oldPlugins)),
zap.Int("new_plugin_count", newPluginCount))
return newPluginCount, nil
}
// closeOldPlugins 关闭旧插件
func (hrm *HotReloadManager) closeOldPlugins(oldPlugins map[string]any) {
hrm.mosdns.logger.Info("🔒 开始关闭旧插件", zap.Int("count", len(oldPlugins)))
closedCount := 0
for tag, p := range oldPlugins {
if closer, ok := p.(io.Closer); ok {
hrm.mosdns.logger.Debug("closing old plugin", zap.String("tag", tag))
if err := closer.Close(); err != nil {
hrm.mosdns.logger.Warn("failed to close old plugin",
zap.String("tag", tag),
zap.Error(err))
} else {
closedCount++
}
}
}
hrm.mosdns.logger.Info("✅ 旧插件关闭完成",
zap.Int("total", len(oldPlugins)),
zap.Int("closed", closedCount))
}
// IsReloading 检查是否正在重载
func (hrm *HotReloadManager) IsReloading() bool {
hrm.mu.RLock()
defer hrm.mu.RUnlock()
return hrm.isReloading
}
// GetConfigPath 获取配置文件路径
func (hrm *HotReloadManager) GetConfigPath() string {
return hrm.configPath
}
// SetConfigPath 设置配置文件路径
func (hrm *HotReloadManager) SetConfigPath(path string) {
hrm.mu.Lock()
defer hrm.mu.Unlock()
hrm.configPath = path
}