wip v1.0.0
This commit is contained in:
192
internal/health/monitor.go
Normal file
192
internal/health/monitor.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package health
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/loveuer/go-alived/pkg/logger"
|
||||
)
|
||||
|
||||
type Monitor struct {
|
||||
checker Checker
|
||||
config *CheckerConfig
|
||||
state *CheckerState
|
||||
log *logger.Logger
|
||||
callbacks []StateChangeCallback
|
||||
|
||||
running bool
|
||||
stopCh chan struct{}
|
||||
wg sync.WaitGroup
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewMonitor(checker Checker, config *CheckerConfig, log *logger.Logger) *Monitor {
|
||||
return &Monitor{
|
||||
checker: checker,
|
||||
config: config,
|
||||
state: &CheckerState{
|
||||
Name: config.Name,
|
||||
Healthy: false,
|
||||
},
|
||||
log: log,
|
||||
callbacks: make([]StateChangeCallback, 0),
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Monitor) Start() {
|
||||
m.mu.Lock()
|
||||
if m.running {
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
m.running = true
|
||||
m.mu.Unlock()
|
||||
|
||||
m.log.Info("[HealthCheck:%s] starting health check monitor (interval=%s, timeout=%s)",
|
||||
m.config.Name, m.config.Interval, m.config.Timeout)
|
||||
|
||||
m.wg.Add(1)
|
||||
go m.checkLoop()
|
||||
}
|
||||
|
||||
func (m *Monitor) Stop() {
|
||||
m.mu.Lock()
|
||||
if !m.running {
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
m.running = false
|
||||
m.mu.Unlock()
|
||||
|
||||
m.log.Info("[HealthCheck:%s] stopping health check monitor", m.config.Name)
|
||||
close(m.stopCh)
|
||||
m.wg.Wait()
|
||||
}
|
||||
|
||||
func (m *Monitor) checkLoop() {
|
||||
defer m.wg.Done()
|
||||
|
||||
ticker := time.NewTicker(m.config.Interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
m.performCheck()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-m.stopCh:
|
||||
return
|
||||
case <-ticker.C:
|
||||
m.performCheck()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Monitor) performCheck() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), m.config.Timeout)
|
||||
defer cancel()
|
||||
|
||||
startTime := time.Now()
|
||||
result := m.checker.Check(ctx)
|
||||
duration := time.Since(startTime)
|
||||
|
||||
m.mu.Lock()
|
||||
oldHealthy := m.state.Healthy
|
||||
stateChanged := m.state.Update(result, m.config.Rise, m.config.Fall)
|
||||
newHealthy := m.state.Healthy
|
||||
callbacks := m.callbacks
|
||||
m.mu.Unlock()
|
||||
|
||||
m.log.Debug("[HealthCheck:%s] check completed: result=%s, duration=%s, healthy=%v",
|
||||
m.config.Name, result, duration, newHealthy)
|
||||
|
||||
if stateChanged {
|
||||
m.log.Info("[HealthCheck:%s] health state changed: %v -> %v (consecutive_ok=%d, consecutive_fail=%d)",
|
||||
m.config.Name, oldHealthy, newHealthy, m.state.ConsecutiveOK, m.state.ConsecutiveFail)
|
||||
|
||||
for _, callback := range callbacks {
|
||||
callback(m.config.Name, oldHealthy, newHealthy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Monitor) OnStateChange(callback StateChangeCallback) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.callbacks = append(m.callbacks, callback)
|
||||
}
|
||||
|
||||
func (m *Monitor) GetState() *CheckerState {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
stateCopy := *m.state
|
||||
return &stateCopy
|
||||
}
|
||||
|
||||
func (m *Monitor) IsHealthy() bool {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.state.Healthy
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
monitors map[string]*Monitor
|
||||
mu sync.RWMutex
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewManager(log *logger.Logger) *Manager {
|
||||
return &Manager{
|
||||
monitors: make(map[string]*Monitor),
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) AddMonitor(monitor *Monitor) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.monitors[monitor.config.Name] = monitor
|
||||
}
|
||||
|
||||
func (m *Manager) GetMonitor(name string) (*Monitor, bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
monitor, ok := m.monitors[name]
|
||||
return monitor, ok
|
||||
}
|
||||
|
||||
func (m *Manager) StartAll() {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
for _, monitor := range m.monitors {
|
||||
monitor.Start()
|
||||
}
|
||||
|
||||
m.log.Info("started %d health check monitor(s)", len(m.monitors))
|
||||
}
|
||||
|
||||
func (m *Manager) StopAll() {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
for _, monitor := range m.monitors {
|
||||
monitor.Stop()
|
||||
}
|
||||
|
||||
m.log.Info("stopped all health check monitors")
|
||||
}
|
||||
|
||||
func (m *Manager) GetAllStates() map[string]*CheckerState {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
states := make(map[string]*CheckerState)
|
||||
for name, monitor := range m.monitors {
|
||||
states[name] = monitor.GetState()
|
||||
}
|
||||
|
||||
return states
|
||||
}
|
||||
Reference in New Issue
Block a user