Implement monitor probe execution, history recording, redirect-safe target checks, and a background runner for due monitors.
110 lines
2.3 KiB
Go
110 lines
2.3 KiB
Go
package service
|
|
|
|
import (
|
|
"log"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/QuantumNous/new-api/model"
|
|
)
|
|
|
|
var (
|
|
channelMonitorRunnerOnce sync.Once
|
|
channelMonitorStopMu sync.Mutex
|
|
channelMonitorStopCh chan struct{}
|
|
channelMonitorStopped bool
|
|
|
|
channelMonitorRunningMu sync.Mutex
|
|
channelMonitorRunning = map[int]struct{}{}
|
|
)
|
|
|
|
func StartChannelMonitorRunner() {
|
|
channelMonitorRunnerOnce.Do(func() {
|
|
channelMonitorStopMu.Lock()
|
|
defer channelMonitorStopMu.Unlock()
|
|
|
|
channelMonitorStopCh = make(chan struct{})
|
|
channelMonitorStopped = false
|
|
go runChannelMonitorLoop(channelMonitorStopCh)
|
|
})
|
|
}
|
|
|
|
func StopChannelMonitorRunner() {
|
|
channelMonitorStopMu.Lock()
|
|
defer channelMonitorStopMu.Unlock()
|
|
|
|
if channelMonitorStopCh != nil && !channelMonitorStopped {
|
|
close(channelMonitorStopCh)
|
|
channelMonitorStopped = true
|
|
}
|
|
}
|
|
|
|
func runChannelMonitorLoop(stopCh <-chan struct{}) {
|
|
ticker := time.NewTicker(time.Minute)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
runDueChannelMonitors()
|
|
case <-stopCh:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func runDueChannelMonitors() {
|
|
monitors, err := model.GetEnabledChannelMonitors()
|
|
if err != nil {
|
|
log.Printf("channel monitor runner: get monitors error: %v", err)
|
|
return
|
|
}
|
|
|
|
now := time.Now()
|
|
for _, monitor := range monitors {
|
|
if !channelMonitorDue(&monitor, now) {
|
|
continue
|
|
}
|
|
if !markChannelMonitorRunning(monitor.Id) {
|
|
continue
|
|
}
|
|
|
|
monitorCopy := monitor
|
|
go func() {
|
|
defer unmarkChannelMonitorRunning(monitorCopy.Id)
|
|
if err := RunMonitorCheck(&monitorCopy); err != nil {
|
|
log.Printf("channel monitor runner: monitor %d check error: %v", monitorCopy.Id, err)
|
|
}
|
|
}()
|
|
}
|
|
}
|
|
|
|
func channelMonitorDue(monitor *model.ChannelMonitor, now time.Time) bool {
|
|
if monitor.LastCheckedAt == nil {
|
|
return true
|
|
}
|
|
interval := monitor.CheckInterval
|
|
if interval <= 0 {
|
|
interval = 300
|
|
}
|
|
return !monitor.LastCheckedAt.Add(time.Duration(interval) * time.Second).After(now)
|
|
}
|
|
|
|
func markChannelMonitorRunning(monitorID int) bool {
|
|
channelMonitorRunningMu.Lock()
|
|
defer channelMonitorRunningMu.Unlock()
|
|
|
|
if _, ok := channelMonitorRunning[monitorID]; ok {
|
|
return false
|
|
}
|
|
channelMonitorRunning[monitorID] = struct{}{}
|
|
return true
|
|
}
|
|
|
|
func unmarkChannelMonitorRunning(monitorID int) {
|
|
channelMonitorRunningMu.Lock()
|
|
defer channelMonitorRunningMu.Unlock()
|
|
|
|
delete(channelMonitorRunning, monitorID)
|
|
}
|