feat:1. 增加AnyTLS类型支持 2.定时5m全量探测节点健康改为2h,并增加配置可调整 3. 修复网页端修改配置文件保存失败问题

This commit is contained in:
zhangyongsheng
2026-03-12 16:21:34 +08:00
parent 5484d6f65a
commit 284977b7a5
11 changed files with 230 additions and 32 deletions
+82 -6
View File
@@ -136,6 +136,14 @@ type Manager struct {
ctx context.Context
cancel context.CancelFunc
logger Logger
// periodic health check control
healthMu sync.Mutex
healthInterval time.Duration
healthTimeout time.Duration
healthTicker *time.Ticker
healthIntervalC chan time.Duration
probeAllInFlight atomic.Bool
}
// Logger interface for logging
@@ -199,20 +207,53 @@ func (m *Manager) StartPeriodicHealthCheck(interval, timeout time.Duration) {
}
return
}
if interval <= 0 {
interval = 2 * time.Hour
}
if timeout <= 0 {
timeout = 10 * time.Second
}
m.healthMu.Lock()
if m.healthIntervalC == nil {
m.healthIntervalC = make(chan time.Duration, 1)
}
m.healthInterval = interval
m.healthTimeout = timeout
if m.healthTicker != nil {
m.healthTicker.Stop()
}
m.healthTicker = time.NewTicker(interval)
ticker := m.healthTicker
intervalC := m.healthIntervalC
m.healthMu.Unlock()
go func() {
// 启动后立即进行一次检查
m.probeAllNodes(timeout)
ticker := time.NewTicker(interval)
defer ticker.Stop()
// 启动后立即进行一次检查(走去重)
m.RequestProbeAllOnce(timeout)
for {
select {
case <-m.ctx.Done():
return
case newInterval := <-intervalC:
if newInterval <= 0 {
newInterval = 2 * time.Hour
}
// 重置 ticker
m.healthMu.Lock()
m.healthInterval = newInterval
if m.healthTicker != nil {
m.healthTicker.Stop()
}
m.healthTicker = time.NewTicker(newInterval)
ticker = m.healthTicker
m.healthMu.Unlock()
if m.logger != nil {
m.logger.Info("periodic health check interval updated: ", newInterval)
}
case <-ticker.C:
m.probeAllNodes(timeout)
m.RequestProbeAllOnce(timeout)
}
}
}()
@@ -222,6 +263,41 @@ func (m *Manager) StartPeriodicHealthCheck(interval, timeout time.Duration) {
}
}
// SetHealthCheckInterval updates the periodic health check interval at runtime.
// It is safe to call before StartPeriodicHealthCheck; it will be applied on start.
func (m *Manager) SetHealthCheckInterval(d time.Duration) {
if d <= 0 {
return
}
m.healthMu.Lock()
m.healthInterval = d
intervalC := m.healthIntervalC
m.healthMu.Unlock()
if intervalC != nil {
select {
case intervalC <- d:
default:
// drop if a newer update is already queued
}
}
}
// RequestProbeAllOnce triggers a full probe round at most once concurrently.
// If another full probe is already running, it returns immediately.
func (m *Manager) RequestProbeAllOnce(timeout time.Duration) {
if !m.probeReady {
return
}
if m.probeAllInFlight.Swap(true) {
return
}
go func() {
defer m.probeAllInFlight.Store(false)
m.probeAllNodes(timeout)
}()
}
// probeAllNodes checks all registered nodes concurrently.
func (m *Manager) probeAllNodes(timeout time.Duration) {
m.mu.RLock()
+24 -14
View File
@@ -213,10 +213,11 @@ type allSettingsResponse struct {
PoolBlacklistDuration string `json:"pool_blacklist_duration"`
// Management
ManagementEnabled bool `json:"management_enabled"`
ManagementListen string `json:"management_listen"`
ManagementProbeTarget string `json:"management_probe_target"`
ManagementPassword string `json:"management_password"`
ManagementEnabled bool `json:"management_enabled"`
ManagementListen string `json:"management_listen"`
ManagementProbeTarget string `json:"management_probe_target"`
ManagementPassword string `json:"management_password"`
ManagementHealthCheckInterval string `json:"management_health_check_interval"`
// Subscription refresh
SubRefreshEnabled bool `json:"sub_refresh_enabled"`
@@ -264,10 +265,11 @@ type allSettingsRequest struct {
PoolBlacklistDuration string `json:"pool_blacklist_duration"`
// Management
ManagementEnabled *bool `json:"management_enabled"`
ManagementListen string `json:"management_listen"`
ManagementProbeTarget string `json:"management_probe_target"`
ManagementPassword string `json:"management_password"`
ManagementEnabled *bool `json:"management_enabled"`
ManagementListen string `json:"management_listen"`
ManagementProbeTarget string `json:"management_probe_target"`
ManagementPassword string `json:"management_password"`
ManagementHealthCheckInterval string `json:"management_health_check_interval"`
// Subscription refresh
SubRefreshEnabled bool `json:"sub_refresh_enabled"`
@@ -326,10 +328,11 @@ func (s *Server) getAllSettings() allSettingsResponse {
PoolFailureThreshold: c.Pool.FailureThreshold,
PoolBlacklistDuration: c.Pool.BlacklistDuration.String(),
ManagementEnabled: mgmtEnabled,
ManagementListen: c.Management.Listen,
ManagementProbeTarget: c.Management.ProbeTarget,
ManagementPassword: c.Management.Password,
ManagementEnabled: mgmtEnabled,
ManagementListen: c.Management.Listen,
ManagementProbeTarget: c.Management.ProbeTarget,
ManagementPassword: c.Management.Password,
ManagementHealthCheckInterval: c.Management.HealthCheckInterval.String(),
SubRefreshEnabled: c.SubscriptionRefresh.Enabled,
SubRefreshInterval: c.SubscriptionRefresh.Interval.String(),
@@ -355,7 +358,7 @@ func (s *Server) updateAllSettings(req allSettingsRequest) error {
req.ListenerProtocol, req.MultiPortProtocol,
req.PoolBlacklistDuration, req.SubRefreshInterval, req.SubRefreshTimeout,
req.SubRefreshHealthCheckTimeout, req.SubRefreshDrainTimeout,
req.GeoIPAutoUpdateInterval,
req.GeoIPAutoUpdateInterval, req.ManagementHealthCheckInterval,
); err != nil {
return fmt.Errorf("参数验证失败: %w", err)
}
@@ -410,6 +413,9 @@ func (s *Server) updateAllSettings(req allSettingsRequest) error {
c.Management.Listen = req.ManagementListen
c.Management.ProbeTarget = strings.TrimSpace(req.ManagementProbeTarget)
c.Management.Password = req.ManagementPassword
if d, err := time.ParseDuration(req.ManagementHealthCheckInterval); err == nil && d > 0 {
c.Management.HealthCheckInterval = d
}
// Subscription refresh
c.SubscriptionRefresh.Enabled = req.SubRefreshEnabled
@@ -456,6 +462,10 @@ func (s *Server) updateAllSettings(req allSettingsRequest) error {
s.logger.Printf("更新探测目标失败: %v", err)
}
}
// 动态更新周期健康检查间隔,使其立即生效
if c.Management.HealthCheckInterval > 0 && s.mgr != nil {
s.mgr.SetHealthCheckInterval(c.Management.HealthCheckInterval)
}
s.logger.Printf("✅ 设置已保存并同步到运行时")
return nil
@@ -1086,7 +1096,7 @@ func (s *Server) handleSettings(w http.ResponseWriter, r *http.Request) {
writeJSON(w, map[string]any{
"message": "设置已保存",
"need_reload": true,
"need_reload": false,
})
default:
w.WriteHeader(http.StatusMethodNotAllowed)