161 lines
4.5 KiB
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
|
|
}
|