perf: use fasthttp for health checks; upgrade go to 1.25.3

This commit is contained in:
yusing
2025-10-17 22:50:13 +08:00
parent 3e73be60a1
commit 5a91db8d10
12 changed files with 172 additions and 144 deletions

View File

@@ -1,21 +1,16 @@
package monitor
import (
"fmt"
"net/http"
"net/url"
"time"
"github.com/bytedance/sonic"
agentPkg "github.com/yusing/godoxy/agent/pkg/agent"
"github.com/yusing/godoxy/internal/types"
httputils "github.com/yusing/goutils/http"
)
type (
AgentProxiedMonitor struct {
agent *agentPkg.AgentConfig
endpointURL string
agent *agentPkg.AgentConfig
query string
*monitor
}
AgentCheckHealthTarget struct {
@@ -51,40 +46,19 @@ func (target *AgentCheckHealthTarget) displayURL() *url.URL {
func NewAgentProxiedMonitor(agent *agentPkg.AgentConfig, config *types.HealthCheckConfig, target *AgentCheckHealthTarget) *AgentProxiedMonitor {
mon := &AgentProxiedMonitor{
agent: agent,
endpointURL: agentPkg.EndpointHealth + "?" + target.buildQuery(),
agent: agent,
query: target.buildQuery(),
}
mon.monitor = newMonitor(target.displayURL(), config, mon.CheckHealth)
return mon
}
func (mon *AgentProxiedMonitor) CheckHealth() (result types.HealthCheckResult, err error) {
startTime := time.Now()
ctx, cancel := mon.ContextWithTimeout("timeout querying agent")
defer cancel()
resp, err := mon.agent.DoHealthCheck(ctx, mon.endpointURL)
if err != nil {
return result, err
}
data, release, err := httputils.ReadAllBody(resp)
resp.Body.Close()
if err != nil {
return result, err
}
defer release(data)
endTime := time.Now()
switch resp.StatusCode {
case http.StatusOK:
err = sonic.Unmarshal(data, &result)
default:
err = fmt.Errorf("HTTP %d %s", resp.StatusCode, data)
}
if err == nil && result.Latency != 0 {
// use godoxy to agent latency
result.Latency = endTime.Sub(startTime)
func (mon *AgentProxiedMonitor) CheckHealth() (types.HealthCheckResult, error) {
resp, err := mon.agent.DoHealthCheck(mon.config.Timeout, mon.query)
result := types.HealthCheckResult{
Healthy: resp.Healthy,
Detail: resp.Detail,
Latency: resp.Latency,
}
return result, err
}

View File

@@ -3,10 +3,10 @@ package monitor
import (
"crypto/tls"
"errors"
"net/http"
"net/url"
"time"
"github.com/valyala/fasthttp"
"github.com/yusing/godoxy/internal/types"
"github.com/yusing/goutils/version"
)
@@ -16,65 +16,53 @@ type HTTPHealthMonitor struct {
method string
}
var pinger = &http.Client{
Transport: &http.Transport{
DisableKeepAlives: true,
ForceAttemptHTTP2: false,
TLSHandshakeTimeout: 3 * time.Second,
ResponseHeaderTimeout: 5 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
MaxIdleConnsPerHost: 1,
IdleConnTimeout: 10 * time.Second,
},
CheckRedirect: func(r *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
var pinger = &fasthttp.Client{
ReadTimeout: 5 * time.Second,
WriteTimeout: 3 * time.Second,
MaxConnDuration: 0,
DisableHeaderNamesNormalizing: true,
DisablePathNormalizing: true,
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
MaxConnsPerHost: 1,
NoDefaultUserAgentHeader: true,
}
func NewHTTPHealthMonitor(url *url.URL, config *types.HealthCheckConfig) *HTTPHealthMonitor {
mon := new(HTTPHealthMonitor)
mon.monitor = newMonitor(url, config, mon.CheckHealth)
if config.UseGet {
mon.method = http.MethodGet
mon.method = fasthttp.MethodGet
} else {
mon.method = http.MethodHead
mon.method = fasthttp.MethodHead
}
return mon
}
func (mon *HTTPHealthMonitor) CheckHealth() (types.HealthCheckResult, error) {
ctx, cancel := mon.ContextWithTimeout("ping request timed out")
defer cancel()
req := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(req)
req, err := http.NewRequestWithContext(
ctx,
mon.method,
mon.url.Load().JoinPath(mon.config.Path).String(),
nil,
)
if err != nil {
return types.HealthCheckResult{}, err
}
req.Close = true
resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseResponse(resp)
req.SetRequestURI(mon.url.Load().JoinPath(mon.config.Path).String())
req.Header.SetMethod(mon.method)
req.Header.Set("User-Agent", "GoDoxy/"+version.Get().String())
req.Header.Set("Accept", "text/plain,text/html,*/*;q=0.8")
req.Header.Set("Accept-Encoding", "identity")
req.Header.Set("Cache-Control", "no-cache")
req.Header.Set("Pragma", "no-cache")
req.SetConnectionClose()
start := time.Now()
resp, respErr := pinger.Do(req)
if respErr == nil {
resp.Body.Close()
}
respErr := pinger.DoTimeout(req, resp, mon.config.Timeout)
lat := time.Since(start)
switch {
case respErr != nil:
// treat tls error as healthy
// treat TLS error as healthy
var tlsErr *tls.CertificateVerificationError
if ok := errors.As(respErr, &tlsErr); !ok {
return types.HealthCheckResult{
@@ -82,10 +70,10 @@ func (mon *HTTPHealthMonitor) CheckHealth() (types.HealthCheckResult, error) {
Detail: respErr.Error(),
}, nil
}
case resp.StatusCode == http.StatusServiceUnavailable:
case resp.StatusCode() == fasthttp.StatusServiceUnavailable:
return types.HealthCheckResult{
Latency: lat,
Detail: resp.Status,
Detail: fasthttp.StatusMessage(resp.StatusCode()),
}, nil
}

View File

@@ -2,7 +2,6 @@ package monitor
import (
"context"
"errors"
"fmt"
"math/rand"
"net/url"
@@ -110,7 +109,7 @@ func (mon *monitor) ContextWithTimeout(cause string) (ctx context.Context, cance
default:
ctx = context.Background()
}
return context.WithTimeoutCause(ctx, mon.config.Timeout, errors.New(cause))
return context.WithTimeoutCause(ctx, mon.config.Timeout, gperr.New(cause))
}
// Start implements task.TaskStarter.