mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
170 lines
4.0 KiB
Go
170 lines
4.0 KiB
Go
package proxy
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"ant-chrome/backend/internal/config"
|
|
)
|
|
|
|
const defaultBrowserPageProbeConcurrency = 8
|
|
|
|
var DefaultBrowserPageProbeConfig = BrowserPageProbeConfig{
|
|
URLs: []string{DefaultSpeedTestURL},
|
|
Timeout: 15 * time.Second,
|
|
Concurrency: defaultBrowserPageProbeConcurrency,
|
|
}
|
|
|
|
type BrowserPageProbeConfig struct {
|
|
URLs []string
|
|
Timeout time.Duration
|
|
Concurrency int
|
|
}
|
|
|
|
type BrowserPageProbeResult struct {
|
|
ProxyId string
|
|
Ok bool
|
|
TotalMs int64
|
|
AverageMs int64
|
|
P95Ms int64
|
|
Bytes int64
|
|
Completed int
|
|
Failed int
|
|
Concurrency int
|
|
Error string
|
|
}
|
|
|
|
func ProbeBrowserPageConnectivity(
|
|
proxyId string,
|
|
proxies []config.BrowserProxy,
|
|
xrayMgr *XrayManager,
|
|
singboxMgr *SingBoxManager,
|
|
cfg *BrowserPageProbeConfig,
|
|
) BrowserPageProbeResult {
|
|
normalized := normalizeBrowserPageProbeConfig(cfg)
|
|
client, err := buildProxyHTTPClient("", proxyId, proxies, xrayMgr, singboxMgr, nil, config.BrowserConnectorXray, normalized.Timeout)
|
|
if err != nil {
|
|
return BrowserPageProbeResult{ProxyId: proxyId, Ok: false, Error: err.Error(), Concurrency: normalized.Concurrency}
|
|
}
|
|
return runBrowserPageProbe(proxyId, client, normalized)
|
|
}
|
|
|
|
func normalizeBrowserPageProbeConfig(cfg *BrowserPageProbeConfig) BrowserPageProbeConfig {
|
|
normalized := DefaultBrowserPageProbeConfig
|
|
normalized.URLs = append([]string{}, DefaultBrowserPageProbeConfig.URLs...)
|
|
if cfg == nil {
|
|
return normalized
|
|
}
|
|
urls := make([]string, 0, len(cfg.URLs))
|
|
for _, rawURL := range cfg.URLs {
|
|
if url := strings.TrimSpace(rawURL); url != "" {
|
|
urls = append(urls, url)
|
|
}
|
|
}
|
|
if len(urls) > 0 {
|
|
normalized.URLs = urls
|
|
}
|
|
if cfg.Timeout > 0 {
|
|
normalized.Timeout = cfg.Timeout
|
|
}
|
|
if cfg.Concurrency > 0 {
|
|
normalized.Concurrency = cfg.Concurrency
|
|
}
|
|
return normalized
|
|
}
|
|
|
|
func runBrowserPageProbe(proxyId string, client *http.Client, cfg BrowserPageProbeConfig) BrowserPageProbeResult {
|
|
startedAt := time.Now()
|
|
latencies := make([]int64, 0, cfg.Concurrency)
|
|
var totalBytes int64
|
|
var firstError string
|
|
var failed int
|
|
var mu sync.Mutex
|
|
var wg sync.WaitGroup
|
|
|
|
for i := 0; i < cfg.Concurrency; i++ {
|
|
url := cfg.URLs[i%len(cfg.URLs)]
|
|
wg.Add(1)
|
|
go func(targetURL string) {
|
|
defer wg.Done()
|
|
requestStartedAt := time.Now()
|
|
resp, err := client.Get(targetURL)
|
|
latencyMs := time.Since(requestStartedAt).Milliseconds()
|
|
if err != nil {
|
|
mu.Lock()
|
|
failed++
|
|
if firstError == "" {
|
|
firstError = err.Error()
|
|
}
|
|
mu.Unlock()
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
bytesRead, readErr := io.Copy(io.Discard, resp.Body)
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if readErr != nil || resp.StatusCode >= http.StatusBadRequest {
|
|
failed++
|
|
if firstError == "" {
|
|
if readErr != nil {
|
|
firstError = readErr.Error()
|
|
} else {
|
|
firstError = resp.Status
|
|
}
|
|
}
|
|
return
|
|
}
|
|
latencies = append(latencies, latencyMs)
|
|
totalBytes += bytesRead
|
|
}(url)
|
|
}
|
|
wg.Wait()
|
|
|
|
completed := len(latencies)
|
|
result := BrowserPageProbeResult{
|
|
ProxyId: proxyId,
|
|
Ok: completed > 0 && failed == 0,
|
|
TotalMs: time.Since(startedAt).Milliseconds(),
|
|
Bytes: totalBytes,
|
|
Completed: completed,
|
|
Failed: failed,
|
|
Concurrency: cfg.Concurrency,
|
|
Error: firstError,
|
|
}
|
|
if completed == 0 {
|
|
if result.Error == "" {
|
|
result.Error = "并发探测全部失败"
|
|
}
|
|
return result
|
|
}
|
|
sort.Slice(latencies, func(i, j int) bool { return latencies[i] < latencies[j] })
|
|
var sum int64
|
|
for _, latency := range latencies {
|
|
sum += latency
|
|
}
|
|
result.AverageMs = sum / int64(completed)
|
|
result.P95Ms = percentileLatency(latencies, 0.95)
|
|
return result
|
|
}
|
|
|
|
func percentileLatency(sortedLatencies []int64, percentile float64) int64 {
|
|
if len(sortedLatencies) == 0 {
|
|
return 0
|
|
}
|
|
if percentile <= 0 {
|
|
return sortedLatencies[0]
|
|
}
|
|
idx := int(float64(len(sortedLatencies))*percentile + 0.5)
|
|
if idx < 1 {
|
|
idx = 1
|
|
}
|
|
if idx > len(sortedLatencies) {
|
|
idx = len(sortedLatencies)
|
|
}
|
|
return sortedLatencies[idx-1]
|
|
}
|