fix: race condition in health monitor

This commit is contained in:
yusing 2025-03-28 07:55:23 +08:00
parent 2b91d99ec6
commit 2e8248cd5b
4 changed files with 22 additions and 16 deletions

View file

@ -7,12 +7,14 @@ import (
"github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
metricslogger "github.com/yusing/go-proxy/internal/net/gphttp/middleware/metrics_logger"
"github.com/yusing/go-proxy/internal/route/routes" "github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/watcher/health" "github.com/yusing/go-proxy/internal/watcher/health"
"github.com/yusing/go-proxy/internal/watcher/health/monitor" "github.com/yusing/go-proxy/internal/watcher/health/monitor"
E "github.com/yusing/go-proxy/internal/error"
) )
type ( type (
@ -96,7 +98,7 @@ func (s *FileServer) Start(parent task.Parent) gperr.Error {
} }
if s.UseHealthCheck() { if s.UseHealthCheck() {
s.Health = monitor.NewFileServerHealthMonitor(s.TargetName(), s.HealthCheck, s.Root) s.Health = monitor.NewFileServerHealthMonitor(s.HealthCheck, s.Root)
if err := s.Health.Start(s.task); err != nil { if err := s.Health.Start(s.task); err != nil {
return err return err
} }

View file

@ -12,10 +12,9 @@ type FileServerHealthMonitor struct {
path string path string
} }
func NewFileServerHealthMonitor(alias string, config *health.HealthCheckConfig, path string) *FileServerHealthMonitor { func NewFileServerHealthMonitor(config *health.HealthCheckConfig, path string) *FileServerHealthMonitor {
mon := &FileServerHealthMonitor{path: path} mon := &FileServerHealthMonitor{path: path}
mon.monitor = newMonitor(nil, config, mon.CheckHealth) mon.monitor = newMonitor(nil, config, mon.CheckHealth)
mon.service = alias
return mon return mon
} }

View file

@ -6,8 +6,7 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/gperr"
E "github.com/yusing/go-proxy/internal/error"
"github.com/yusing/go-proxy/internal/logging" "github.com/yusing/go-proxy/internal/logging"
"github.com/yusing/go-proxy/internal/metrics" "github.com/yusing/go-proxy/internal/metrics"
"github.com/yusing/go-proxy/internal/net/types" "github.com/yusing/go-proxy/internal/net/types"
@ -26,7 +25,7 @@ type (
url atomic.Value[*types.URL] url atomic.Value[*types.URL]
status atomic.Value[health.Status] status atomic.Value[health.Status]
lastResult *health.HealthCheckResult lastResult atomic.Value[*health.HealthCheckResult]
checkHealth HealthCheckFunc checkHealth HealthCheckFunc
startTime time.Time startTime time.Time
@ -140,10 +139,11 @@ func (mon *monitor) Uptime() time.Duration {
// Latency implements HealthMonitor. // Latency implements HealthMonitor.
func (mon *monitor) Latency() time.Duration { func (mon *monitor) Latency() time.Duration {
if mon.lastResult == nil { res := mon.lastResult.Load()
if res == nil {
return 0 return 0
} }
return mon.lastResult.Latency return res.Latency
} }
// Name implements HealthMonitor. // Name implements HealthMonitor.
@ -159,7 +159,13 @@ func (mon *monitor) String() string {
// MarshalJSON implements json.Marshaler of HealthMonitor. // MarshalJSON implements json.Marshaler of HealthMonitor.
func (mon *monitor) MarshalJSON() ([]byte, error) { func (mon *monitor) MarshalJSON() ([]byte, error) {
res := mon.lastResult res := mon.lastResult.Load()
if res == nil {
res = &health.HealthCheckResult{
Healthy: true,
}
}
return (&JSONRepresentation{ return (&JSONRepresentation{
Name: mon.service, Name: mon.service,
Config: mon.config, Config: mon.config,
@ -185,7 +191,7 @@ func (mon *monitor) checkUpdateHealth() error {
return nil return nil
} }
mon.lastResult = result mon.lastResult.Store(result)
var status health.Status var status health.Status
if result.Healthy { if result.Healthy {
status = health.StatusHealthy status = health.StatusHealthy

View file

@ -36,14 +36,13 @@ func (mon *RawHealthMonitor) CheckHealth() (result *health.HealthCheckResult, er
url := mon.url.Load() url := mon.url.Load()
start := time.Now() start := time.Now()
conn, dialErr := mon.dialer.DialContext(ctx, url.Scheme, url.Host) conn, dialErr := mon.dialer.DialContext(ctx, url.Scheme, url.Host)
result = &health.HealthCheckResult{ result = new(health.HealthCheckResult)
Latency: time.Since(start),
}
if dialErr != nil { if dialErr != nil {
result.Detail = dialErr.Error() result.Detail = dialErr.Error()
return return
} }
conn.Close() defer conn.Close()
result.Latency = time.Since(start)
result.Healthy = true result.Healthy = true
return return
} }