From 2e8248cd5be74048163eabcaabf86e93442c07ff Mon Sep 17 00:00:00 2001 From: yusing Date: Fri, 28 Mar 2025 07:55:23 +0800 Subject: [PATCH] fix: race condition in health monitor --- internal/route/fileserver.go | 8 +++++--- internal/watcher/health/monitor/fileserver.go | 3 +-- internal/watcher/health/monitor/monitor.go | 20 ++++++++++++------- internal/watcher/health/monitor/raw.go | 7 +++---- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/internal/route/fileserver.go b/internal/route/fileserver.go index b39847d..ed75e9b 100644 --- a/internal/route/fileserver.go +++ b/internal/route/fileserver.go @@ -7,12 +7,14 @@ import ( "github.com/yusing/go-proxy/internal/common" "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/task" "github.com/yusing/go-proxy/internal/watcher/health" "github.com/yusing/go-proxy/internal/watcher/health/monitor" - - E "github.com/yusing/go-proxy/internal/error" ) type ( @@ -96,7 +98,7 @@ func (s *FileServer) Start(parent task.Parent) gperr.Error { } 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 { return err } diff --git a/internal/watcher/health/monitor/fileserver.go b/internal/watcher/health/monitor/fileserver.go index e62e392..67a973b 100644 --- a/internal/watcher/health/monitor/fileserver.go +++ b/internal/watcher/health/monitor/fileserver.go @@ -12,10 +12,9 @@ type FileServerHealthMonitor struct { 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.monitor = newMonitor(nil, config, mon.CheckHealth) - mon.service = alias return mon } diff --git a/internal/watcher/health/monitor/monitor.go b/internal/watcher/health/monitor/monitor.go index 4349b70..7fa3420 100644 --- a/internal/watcher/health/monitor/monitor.go +++ b/internal/watcher/health/monitor/monitor.go @@ -6,8 +6,7 @@ import ( "fmt" "time" - "github.com/yusing/go-proxy/internal/common" - E "github.com/yusing/go-proxy/internal/error" + "github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/logging" "github.com/yusing/go-proxy/internal/metrics" "github.com/yusing/go-proxy/internal/net/types" @@ -26,7 +25,7 @@ type ( url atomic.Value[*types.URL] status atomic.Value[health.Status] - lastResult *health.HealthCheckResult + lastResult atomic.Value[*health.HealthCheckResult] checkHealth HealthCheckFunc startTime time.Time @@ -140,10 +139,11 @@ func (mon *monitor) Uptime() time.Duration { // Latency implements HealthMonitor. func (mon *monitor) Latency() time.Duration { - if mon.lastResult == nil { + res := mon.lastResult.Load() + if res == nil { return 0 } - return mon.lastResult.Latency + return res.Latency } // Name implements HealthMonitor. @@ -159,7 +159,13 @@ func (mon *monitor) String() string { // MarshalJSON implements json.Marshaler of HealthMonitor. func (mon *monitor) MarshalJSON() ([]byte, error) { - res := mon.lastResult + res := mon.lastResult.Load() + if res == nil { + res = &health.HealthCheckResult{ + Healthy: true, + } + } + return (&JSONRepresentation{ Name: mon.service, Config: mon.config, @@ -185,7 +191,7 @@ func (mon *monitor) checkUpdateHealth() error { return nil } - mon.lastResult = result + mon.lastResult.Store(result) var status health.Status if result.Healthy { status = health.StatusHealthy diff --git a/internal/watcher/health/monitor/raw.go b/internal/watcher/health/monitor/raw.go index 490cd22..838d47a 100644 --- a/internal/watcher/health/monitor/raw.go +++ b/internal/watcher/health/monitor/raw.go @@ -36,14 +36,13 @@ func (mon *RawHealthMonitor) CheckHealth() (result *health.HealthCheckResult, er url := mon.url.Load() start := time.Now() conn, dialErr := mon.dialer.DialContext(ctx, url.Scheme, url.Host) - result = &health.HealthCheckResult{ - Latency: time.Since(start), - } + result = new(health.HealthCheckResult) if dialErr != nil { result.Detail = dialErr.Error() return } - conn.Close() + defer conn.Close() + result.Latency = time.Since(start) result.Healthy = true return }