2025-12-31 08:50:12 +08:00
|
|
|
|
package repository
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
2026-03-02 15:53:26 +08:00
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/pkg/proxyurl"
|
|
|
|
|
|
|
2025-12-31 08:50:12 +08:00
|
|
|
|
"github.com/imroc/req/v3"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// reqClientOptions 定义 req 客户端的构建参数
|
|
|
|
|
|
type reqClientOptions struct {
|
|
|
|
|
|
ProxyURL string // 代理 URL(支持 http/https/socks5)
|
|
|
|
|
|
Timeout time.Duration // 请求超时时间
|
|
|
|
|
|
Impersonate bool // 是否模拟 Chrome 浏览器指纹
|
2026-01-19 19:50:57 +08:00
|
|
|
|
ForceHTTP2 bool // 是否强制使用 HTTP/2
|
2025-12-31 08:50:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// sharedReqClients 存储按配置参数缓存的 req 客户端实例
|
|
|
|
|
|
//
|
|
|
|
|
|
// 性能优化说明:
|
|
|
|
|
|
// 原实现在每次 OAuth 刷新时都创建新的 req.Client:
|
|
|
|
|
|
// 1. claude_oauth_service.go: 每次刷新创建新客户端
|
|
|
|
|
|
// 2. openai_oauth_service.go: 每次刷新创建新客户端
|
|
|
|
|
|
// 3. gemini_oauth_client.go: 每次刷新创建新客户端
|
|
|
|
|
|
//
|
|
|
|
|
|
// 新实现使用 sync.Map 缓存客户端:
|
|
|
|
|
|
// 1. 相同配置(代理+超时+模拟设置)复用同一客户端
|
|
|
|
|
|
// 2. 复用底层连接池,减少 TLS 握手开销
|
|
|
|
|
|
// 3. LoadOrStore 保证并发安全,避免重复创建
|
|
|
|
|
|
var sharedReqClients sync.Map
|
|
|
|
|
|
|
|
|
|
|
|
// getSharedReqClient 获取共享的 req 客户端实例
|
|
|
|
|
|
// 性能优化:相同配置复用同一客户端,避免重复创建
|
2026-03-02 15:53:26 +08:00
|
|
|
|
func getSharedReqClient(opts reqClientOptions) (*req.Client, error) {
|
2025-12-31 08:50:12 +08:00
|
|
|
|
key := buildReqClientKey(opts)
|
|
|
|
|
|
if cached, ok := sharedReqClients.Load(key); ok {
|
2025-12-31 14:51:58 +08:00
|
|
|
|
if c, ok := cached.(*req.Client); ok {
|
2026-03-02 15:53:26 +08:00
|
|
|
|
return c, nil
|
2025-12-31 14:51:58 +08:00
|
|
|
|
}
|
2025-12-31 08:50:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
client := req.C().SetTimeout(opts.Timeout)
|
2026-01-19 19:50:57 +08:00
|
|
|
|
if opts.ForceHTTP2 {
|
|
|
|
|
|
client = client.EnableForceHTTP2()
|
|
|
|
|
|
}
|
2025-12-31 08:50:12 +08:00
|
|
|
|
if opts.Impersonate {
|
|
|
|
|
|
client = client.ImpersonateChrome()
|
|
|
|
|
|
}
|
2026-03-02 15:53:26 +08:00
|
|
|
|
trimmed, _, err := proxyurl.Parse(opts.ProxyURL)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
if trimmed != "" {
|
|
|
|
|
|
client.SetProxyURL(trimmed)
|
2025-12-31 08:50:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
actual, _ := sharedReqClients.LoadOrStore(key, client)
|
2025-12-31 14:51:58 +08:00
|
|
|
|
if c, ok := actual.(*req.Client); ok {
|
2026-03-02 15:53:26 +08:00
|
|
|
|
return c, nil
|
2025-12-31 14:51:58 +08:00
|
|
|
|
}
|
2026-03-02 15:53:26 +08:00
|
|
|
|
return client, nil
|
2025-12-31 08:50:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func buildReqClientKey(opts reqClientOptions) string {
|
2026-01-19 19:50:57 +08:00
|
|
|
|
return fmt.Sprintf("%s|%s|%t|%t",
|
2025-12-31 08:50:12 +08:00
|
|
|
|
strings.TrimSpace(opts.ProxyURL),
|
|
|
|
|
|
opts.Timeout.String(),
|
|
|
|
|
|
opts.Impersonate,
|
2026-01-19 19:50:57 +08:00
|
|
|
|
opts.ForceHTTP2,
|
2025-12-31 08:50:12 +08:00
|
|
|
|
)
|
|
|
|
|
|
}
|