package service import ( "context" "fmt" "net/http" "strings" "time" "zencoder-2api/internal/service/provider" ) // ProxyRequestOptions 代理请求选项 type ProxyRequestOptions struct { UseProxy bool // 是否使用代理 MaxRetries int // 最大重试次数 RetryDelay time.Duration // 重试延迟 OnError func(error) bool // 错误判断函数,返回true表示需要重试 } // DefaultProxyRequestOptions 默认代理请求选项 func DefaultProxyRequestOptions() ProxyRequestOptions { return ProxyRequestOptions{ UseProxy: true, MaxRetries: 3, RetryDelay: time.Second, OnError: isNetworkError, } } // isNetworkError 判断是否为网络错误(可重试的错误) func isNetworkError(err error) bool { if err == nil { return false } errStr := err.Error() // 网络连接错误 if strings.Contains(errStr, "connection refused") || strings.Contains(errStr, "connection reset") || strings.Contains(errStr, "connection timed out") || strings.Contains(errStr, "timeout") || strings.Contains(errStr, "network is unreachable") || strings.Contains(errStr, "no such host") || strings.Contains(errStr, "dial tcp") || strings.Contains(errStr, "i/o timeout") || strings.Contains(errStr, "EOF") { return true } // SOCKS代理相关错误 if strings.Contains(errStr, "socks connect") || strings.Contains(errStr, "proxy") { return true } return false } // DoRequestWithProxyRetry 执行带代理重试的HTTP请求 func DoRequestWithProxyRetry(ctx context.Context, req *http.Request, originalProxy string, options ProxyRequestOptions) (*http.Response, error) { // 首先尝试使用原始代理(如果有的话) client := provider.NewHTTPClient(originalProxy, 0) resp, err := client.Do(req) if err == nil { // 请求成功,返回结果 return resp, nil } // 检查错误是否可重试 if !options.OnError(err) { return nil, err } // 如果不使用代理池,直接返回错误 if !options.UseProxy { return nil, err } proxyPool := provider.GetProxyPool() if !proxyPool.HasProxies() { return nil, fmt.Errorf("原始请求失败且无可用代理: %v", err) } var lastErr error = err // 使用代理池进行重试 for i := 0; i < options.MaxRetries; i++ { // 获取下一个代理 proxyURL := proxyPool.GetNextProxy() if proxyURL == "" { break } // 创建使用代理的HTTP客户端 proxyClient, clientErr := provider.NewHTTPClientWithProxy(proxyURL, 0) if clientErr != nil { lastErr = clientErr time.Sleep(options.RetryDelay) continue } // 克隆请求(因为request body可能已经被消费) newReq := req.Clone(ctx) resp, err := proxyClient.Do(newReq) if err == nil { // 请求成功 return resp, nil } // 检查错误是否继续重试 if !options.OnError(err) { return nil, err } lastErr = err time.Sleep(options.RetryDelay) } return nil, fmt.Errorf("所有代理重试均失败,最后错误: %v", lastErr) } // CreateHTTPClientWithFallback 创建支持代理fallback的HTTP客户端 func CreateHTTPClientWithFallback(originalProxy string, useProxyPool bool) *http.Client { // 如果不使用代理池,使用原始逻辑 if !useProxyPool { return provider.NewHTTPClient(originalProxy, 0) } // 如果有原始代理,先尝试原始代理 if originalProxy != "" { client, err := provider.NewHTTPClientWithProxy(originalProxy, 0) if err == nil { return client } } // 使用代理池 return provider.NewHTTPClientWithPoolProxy(true, 0) }