将 Worker 发现、路由画像、容量与执行传输抽象为平台无关接口,新增 Kubernetes 和静态容量适配器,并以 shadow 模式接入生产配置。 实现网络与容量评分、路由防抖、池队列、同步 Worker 租约、一次性执行令牌,以及提交状态不明时禁止重复分配的安全语义。 新增 0105 兼容迁移、管理接口、指标、OpenAPI 和回归测试。已执行全量 Go 测试、go vet、OpenAPI、迁移安全、Compose 与 Kustomize 验证。
102 lines
2.3 KiB
Go
102 lines
2.3 KiB
Go
package executionpool
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"net/http"
|
|
"net/http/httptrace"
|
|
"time"
|
|
)
|
|
|
|
type ProbeTarget struct {
|
|
RouteProfile RouteProfile
|
|
URL string
|
|
Method string
|
|
Timeout time.Duration
|
|
}
|
|
|
|
type ProbeResult struct {
|
|
Reachable bool
|
|
StatusCode int
|
|
DNSDuration time.Duration
|
|
TCPDuration time.Duration
|
|
TLSDuration time.Duration
|
|
FirstByte time.Duration
|
|
SampledAt time.Time
|
|
ErrorClass string
|
|
}
|
|
|
|
type NetworkProbe interface {
|
|
Probe(context.Context, ProbeTarget) ProbeResult
|
|
}
|
|
|
|
type HTTPProbe struct {
|
|
Client *http.Client
|
|
Now func() time.Time
|
|
}
|
|
|
|
func (p HTTPProbe) Probe(ctx context.Context, target ProbeTarget) ProbeResult {
|
|
result := ProbeResult{}
|
|
now := time.Now
|
|
if p.Now != nil {
|
|
now = p.Now
|
|
}
|
|
result.SampledAt = now()
|
|
method := target.Method
|
|
if method == "" {
|
|
method = http.MethodHead
|
|
}
|
|
timeout := target.Timeout
|
|
if timeout <= 0 {
|
|
timeout = 3 * time.Second
|
|
}
|
|
probeCtx, cancel := context.WithTimeout(ctx, timeout)
|
|
defer cancel()
|
|
var dnsStart, tcpStart, tlsStart time.Time
|
|
requestStart := now()
|
|
trace := &httptrace.ClientTrace{
|
|
DNSStart: func(httptrace.DNSStartInfo) { dnsStart = now() },
|
|
DNSDone: func(httptrace.DNSDoneInfo) {
|
|
if !dnsStart.IsZero() {
|
|
result.DNSDuration = now().Sub(dnsStart)
|
|
}
|
|
},
|
|
ConnectStart: func(_, _ string) { tcpStart = now() },
|
|
ConnectDone: func(_, _ string, _ error) {
|
|
if !tcpStart.IsZero() {
|
|
result.TCPDuration = now().Sub(tcpStart)
|
|
}
|
|
},
|
|
TLSHandshakeStart: func() { tlsStart = now() },
|
|
TLSHandshakeDone: func(tls.ConnectionState, error) {
|
|
if !tlsStart.IsZero() {
|
|
result.TLSDuration = now().Sub(tlsStart)
|
|
}
|
|
},
|
|
GotFirstResponseByte: func() { result.FirstByte = now().Sub(requestStart) },
|
|
}
|
|
request, err := http.NewRequestWithContext(httptrace.WithClientTrace(probeCtx, trace), method, target.URL, nil)
|
|
if err != nil {
|
|
result.ErrorClass = "invalid_target"
|
|
return result
|
|
}
|
|
client := p.Client
|
|
if client == nil {
|
|
client = http.DefaultClient
|
|
}
|
|
response, err := client.Do(request)
|
|
if err != nil {
|
|
if probeCtx.Err() != nil {
|
|
result.ErrorClass = "timeout"
|
|
} else {
|
|
result.ErrorClass = "network"
|
|
}
|
|
return result
|
|
}
|
|
defer response.Body.Close()
|
|
result.StatusCode = response.StatusCode
|
|
// Any HTTP response, including 401/403/404/405, proves transport reachability.
|
|
result.Reachable = true
|
|
return result
|
|
}
|