From 933ea0cddc9ee9d691cd3b424136ad98f760f6db Mon Sep 17 00:00:00 2001 From: Seefs <40468931+seefs001@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:30:08 +0800 Subject: [PATCH] fix: add relay idle connection timeout config (#5309) --- .env.example | 2 ++ README.md | 1 + common/constants.go | 1 + common/init.go | 1 + docker-compose.yml | 1 + service/http_client.go | 3 +++ 6 files changed, 9 insertions(+) diff --git a/.env.example b/.env.example index b152c2a10..e02b1f538 100644 --- a/.env.example +++ b/.env.example @@ -56,6 +56,8 @@ # 对话超时设置 # 所有请求超时时间,单位秒,默认为0,表示不限制 # RELAY_TIMEOUT=0 +# Relay HTTP 客户端空闲连接超时时间,单位秒,默认跟随 Go 标准库,设置为0表示不限制 +# RELAY_IDLE_CONN_TIMEOUT=90 # 流模式无响应超时时间,单位秒,如果出现空补全可以尝试改为更大值 # STREAMING_TIMEOUT=300 diff --git a/README.md b/README.md index ab6f1499e..c5b5e322a 100644 --- a/README.md +++ b/README.md @@ -316,6 +316,7 @@ docker run --name new-api -d --restart always \ | `CRYPTO_SECRET` | Encryption secret (required for Redis) | - | | `SQL_DSN` | Database connection string | - | | `REDIS_CONN_STRING` | Redis connection string | - | +| `RELAY_IDLE_CONN_TIMEOUT` | Idle keep-alive timeout for relay HTTP clients, seconds. Defaults to Go standard library behavior; set `0` to disable | `90` | | `STREAMING_TIMEOUT` | Streaming timeout (seconds) | `300` | | `STREAM_SCANNER_MAX_BUFFER_MB` | Max per-line buffer (MB) for the stream scanner; increase when upstream sends huge image/base64 payloads | `64` | | `MAX_REQUEST_BODY_MB` | Max request body size (MB, counted **after decompression**; prevents huge requests/zip bombs from exhausting memory). Exceeding it returns `413` | `32` | diff --git a/common/constants.go b/common/constants.go index c7d5637c8..b0386178e 100644 --- a/common/constants.go +++ b/common/constants.go @@ -170,6 +170,7 @@ var BatchUpdateInterval int var RelayTimeout int // unit is second +var RelayIdleConnTimeout int // unit is second var RelayMaxIdleConns int var RelayMaxIdleConnsPerHost int diff --git a/common/init.go b/common/init.go index 35b4c6be1..138bc8ffd 100644 --- a/common/init.go +++ b/common/init.go @@ -102,6 +102,7 @@ func InitEnv() { SyncFrequency = GetEnvOrDefault("SYNC_FREQUENCY", 60) BatchUpdateInterval = GetEnvOrDefault("BATCH_UPDATE_INTERVAL", 5) RelayTimeout = GetEnvOrDefault("RELAY_TIMEOUT", 0) + RelayIdleConnTimeout = GetEnvOrDefault("RELAY_IDLE_CONN_TIMEOUT", 90) RelayMaxIdleConns = GetEnvOrDefault("RELAY_MAX_IDLE_CONNS", 500) RelayMaxIdleConnsPerHost = GetEnvOrDefault("RELAY_MAX_IDLE_CONNS_PER_HOST", 100) diff --git a/docker-compose.yml b/docker-compose.yml index be8c885b1..48c071f98 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,6 +34,7 @@ services: - BATCH_UPDATE_ENABLED=true # 是否启用批量更新 (Whether to enable batch update) - NODE_NAME=new-api-node-1 # 节点名称,用于审计日志中标识节点身份;多节点/容器部署时建议设置 (Node name used in audit logs; recommended when running multiple instances or in containers) # - STREAMING_TIMEOUT=300 # 流模式无响应超时时间,单位秒,默认120秒,如果出现空补全可以尝试改为更大值 (Streaming timeout in seconds, default is 120s. Increase if experiencing empty completions) +# - RELAY_IDLE_CONN_TIMEOUT=90 # Relay HTTP 客户端空闲连接超时时间,单位秒,默认跟随 Go 标准库,设置为0表示不限制 (Relay HTTP client idle keep-alive timeout in seconds, defaults to Go standard library; set 0 to disable) # - SESSION_SECRET=random_string # 多机部署时设置,必须修改这个随机字符串!! (multi-node deployment, set this to a random string!!!!!!!) # - SYNC_FREQUENCY=60 # Uncomment if regular database syncing is needed # - GOOGLE_ANALYTICS_ID=G-XXXXXXXXXX # Google Analytics 的测量 ID (Google Analytics Measurement ID) diff --git a/service/http_client.go b/service/http_client.go index 2c3168f24..670dbc5fe 100644 --- a/service/http_client.go +++ b/service/http_client.go @@ -37,6 +37,7 @@ func InitHttpClient() { transport := &http.Transport{ MaxIdleConns: common.RelayMaxIdleConns, MaxIdleConnsPerHost: common.RelayMaxIdleConnsPerHost, + IdleConnTimeout: time.Duration(common.RelayIdleConnTimeout) * time.Second, ForceAttemptHTTP2: true, Proxy: http.ProxyFromEnvironment, // Support HTTP_PROXY, HTTPS_PROXY, NO_PROXY env vars } @@ -108,6 +109,7 @@ func NewProxyHttpClient(proxyURL string) (*http.Client, error) { transport := &http.Transport{ MaxIdleConns: common.RelayMaxIdleConns, MaxIdleConnsPerHost: common.RelayMaxIdleConnsPerHost, + IdleConnTimeout: time.Duration(common.RelayIdleConnTimeout) * time.Second, ForceAttemptHTTP2: true, Proxy: http.ProxyURL(parsedURL), } @@ -147,6 +149,7 @@ func NewProxyHttpClient(proxyURL string) (*http.Client, error) { transport := &http.Transport{ MaxIdleConns: common.RelayMaxIdleConns, MaxIdleConnsPerHost: common.RelayMaxIdleConnsPerHost, + IdleConnTimeout: time.Duration(common.RelayIdleConnTimeout) * time.Second, ForceAttemptHTTP2: true, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { return dialer.Dial(network, addr)