proxy-pool/internal/controller/bootstrap/provider.go
2026-08-02 14:21:18 +08:00

128 lines
4.6 KiB
Go

package bootstrap
import (
"errors"
"math"
"sort"
"strings"
"time"
"proxy-pool/internal/adapters/providerapi"
"proxy-pool/internal/config"
"proxy-pool/internal/controller/pool"
controllerProvider "proxy-pool/internal/controller/provider"
)
var ErrProviderRuntime = errors.New("invalid Provider runtime configuration")
func newProviderFleet(configuration *config.Config, opened ports) (*controllerProvider.Fleet, error) {
if configuration == nil {
return nil, ErrProviderRuntime
}
names := make([]string, 0, len(configuration.Upstreams))
for name, upstream := range configuration.Upstreams {
if upstream.Enabled {
names = append(names, name)
}
}
if len(names) == 0 {
return nil, nil
}
builder, err := providerRuntimeBuilder(opened)
if err != nil {
return nil, err
}
sort.Strings(names)
runtimes := make([]*controllerProvider.UpstreamRuntime, 0, len(names))
for _, name := range names {
runtime, err := builder(name, configuration.Upstreams[name])
if err != nil {
return nil, err
}
runtimes = append(runtimes, runtime)
}
return controllerProvider.NewFleet(runtimes...)
}
type buildUpstreamRuntime func(string, config.Upstream) (*controllerProvider.UpstreamRuntime, error)
func providerRuntimeBuilder(opened ports) (buildUpstreamRuntime, error) {
if nilInterface(opened.coordinator) || nilInterface(opened.activity) || nilInterface(opened.credentials) {
return nil, ErrProviderRuntime
}
results := opened.providerResults
if nilInterface(results) {
results = discardProviderResults{}
}
return func(name string, upstream config.Upstream) (*controllerProvider.UpstreamRuntime, error) {
upstream.Enabled = true
mapped, err := providerRuntimeConfig(name, upstream)
if err != nil {
return nil, err
}
adapter, err := providerapi.NewHTTPAdapter(upstream.API, upstream.Fetch, nil)
if err != nil {
return nil, err
}
parser, err := providerapi.NewTemplateParser(name, upstream, opened.credentials)
if err != nil {
return nil, err
}
return controllerProvider.NewUpstreamRuntime(mapped, controllerProvider.UpstreamRuntimeDependencies{
Coordinator: opened.coordinator,
Inventory: opened.activity,
Adapter: adapter,
Parser: parser,
Activity: opened.activity,
Results: results,
Capacity: opened.capacityMetrics,
})
}, nil
}
func providerRuntimeConfig(
upstreamID string,
upstream config.Upstream,
) (controllerProvider.UpstreamRuntimeConfig, error) {
if strings.TrimSpace(upstreamID) != upstreamID || upstreamID == "" || !upstream.Enabled ||
upstream.Fetch.EstimatedIPsPerCall <= 0 || upstream.Capacity.MaxConcurrencyPerProxy <= 0 ||
upstream.Pool.MaxSize > config.MaximumPoolSize ||
int64(upstream.Fetch.EstimatedIPsPerCall) > controllerProvider.MaximumCoordinationInteger ||
int64(upstream.Fetch.MaxInFlight) > controllerProvider.MaximumCoordinationInteger ||
int64(upstream.Fetch.MaxTotal) > controllerProvider.MaximumCoordinationInteger ||
int64(upstream.Fetch.EstimatedIPsPerCall) > math.MaxInt64/int64(upstream.Capacity.MaxConcurrencyPerProxy) {
return controllerProvider.UpstreamRuntimeConfig{}, ErrProviderRuntime
}
expectedSlots := int64(upstream.Fetch.EstimatedIPsPerCall) * int64(upstream.Capacity.MaxConcurrencyPerProxy)
return controllerProvider.UpstreamRuntimeConfig{
Provider: controllerProvider.Config{
UpstreamID: upstreamID,
RequestInterval: time.Duration(upstream.Fetch.RequestInterval),
Timeout: time.Duration(upstream.Fetch.Timeout),
MaxAttempts: upstream.Fetch.MaxAttempts,
MaxInFlight: upstream.Fetch.MaxInFlight,
MaxTotal: int64(upstream.Fetch.MaxTotal),
MaxSize: upstream.Pool.MaxSize,
TTL: time.Duration(upstream.Lifecycle.TTL),
AllocationSafetyMargin: time.Duration(upstream.Lifecycle.AllocationSafetyMargin),
Retry: controllerProvider.RetryConfig{
Initial: time.Duration(upstream.Fetch.Retry.Initial),
Max: time.Duration(upstream.Fetch.Retry.Max),
Jitter: upstream.Fetch.Retry.Jitter,
},
},
ReconcilePolicy: pool.ReconcilePolicy{
MinimumAvailableSlots: upstream.Refill.MinimumAvailableSlots,
TargetAvailableSlots: upstream.Refill.TargetAvailableSlots,
ExpectedPerFetch: upstream.Fetch.EstimatedIPsPerCall,
ExpectedSlotsPerFetch: expectedSlots,
SafetyMargin: time.Duration(upstream.Lifecycle.AllocationSafetyMargin),
},
ReconcileInterval: time.Duration(upstream.Refill.ReconcileInterval),
}, nil
}
type discardProviderResults struct{}
func (discardProviderResults) Record(controllerProvider.Result) {}