diff --git a/.env.example b/.env.example index 62f7fcfb..4d77eb53 100644 --- a/.env.example +++ b/.env.example @@ -22,6 +22,12 @@ REDIS_PASSWORD= REDIS_DB=0 REDIS_ENABLE_TLS= +# 🔗 会话管理配置 +# 粘性会话TTL配置(小时),默认1小时 +STICKY_SESSION_TTL_HOURS=1 +# 续期阈值(分钟),默认0分钟(不续期) +STICKY_SESSION_RENEWAL_THRESHOLD_MINUTES=0 + # 🎯 Claude API 配置 CLAUDE_API_URL=https://api.anthropic.com/v1/messages CLAUDE_API_VERSION=2023-06-01 diff --git a/config/config.example.js b/config/config.example.js index a5aa2c7b..1fdcdf7c 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -34,10 +34,10 @@ const config = { // 🔗 会话管理配置 session: { - // 粘性会话TTL配置(天) - stickyTtlDays: parseInt(process.env.STICKY_SESSION_TTL_DAYS) || 15, - // 续期阈值(天) - renewalThresholdDays: parseInt(process.env.STICKY_SESSION_RENEWAL_THRESHOLD_DAYS) || 14 + // 粘性会话TTL配置(小时),默认1小时 + stickyTtlHours: parseFloat(process.env.STICKY_SESSION_TTL_HOURS) || 1, + // 续期阈值(分钟),默认0分钟(不续期) + renewalThresholdMinutes: parseInt(process.env.STICKY_SESSION_RENEWAL_THRESHOLD_MINUTES) || 0 }, // 🎯 Claude API配置 diff --git a/src/models/redis.js b/src/models/redis.js index c862fb57..7ef64ebf 100644 --- a/src/models/redis.js +++ b/src/models/redis.js @@ -1358,7 +1358,8 @@ class RedisClient { // 🔗 会话sticky映射管理 async setSessionAccountMapping(sessionHash, accountId, ttl = null) { const appConfig = require('../../config/config') - const defaultTTL = ttl !== null ? ttl : (appConfig.session?.stickyTtlDays || 15) * 24 * 60 * 60 + // 从配置读取TTL(小时),转换为秒,默认1小时 + const defaultTTL = ttl !== null ? ttl : (appConfig.session?.stickyTtlHours || 1) * 60 * 60 const key = `sticky_session:${sessionHash}` await this.client.set(key, accountId, 'EX', defaultTTL) } @@ -1374,11 +1375,16 @@ class RedisClient { const key = `sticky_session:${sessionHash}` // 📊 从配置获取参数 - const ttlDays = appConfig.session?.stickyTtlDays || 15 - const thresholdDays = appConfig.session?.renewalThresholdDays || 14 + const ttlHours = appConfig.session?.stickyTtlHours || 1 // 小时,默认1小时 + const thresholdMinutes = appConfig.session?.renewalThresholdMinutes || 0 // 分钟,默认0(不续期) - const fullTTL = ttlDays * 24 * 60 * 60 // 转换为秒 - const renewalThreshold = thresholdDays * 24 * 60 * 60 // 转换为秒 + // 如果阈值为0,不执行续期 + if (thresholdMinutes === 0) { + return true + } + + const fullTTL = ttlHours * 60 * 60 // 转换为秒 + const renewalThreshold = thresholdMinutes * 60 // 转换为秒 try { // 获取当前剩余TTL(秒) @@ -1398,14 +1404,14 @@ class RedisClient { if (remainingTTL < renewalThreshold) { await this.client.expire(key, fullTTL) logger.debug( - `🔄 Renewed sticky session TTL: ${sessionHash} (was ${Math.round(remainingTTL / 24 / 3600)}d, renewed to ${ttlDays}d)` + `🔄 Renewed sticky session TTL: ${sessionHash} (was ${Math.round(remainingTTL / 60)}min, renewed to ${ttlHours}h)` ) return true } // 剩余时间充足,无需续期 logger.debug( - `✅ Sticky session TTL sufficient: ${sessionHash} (remaining ${Math.round(remainingTTL / 24 / 3600)}d)` + `✅ Sticky session TTL sufficient: ${sessionHash} (remaining ${Math.round(remainingTTL / 60)}min)` ) return true } catch (error) { diff --git a/src/services/claudeAccountService.js b/src/services/claudeAccountService.js index 29dee2bd..353fe3cf 100644 --- a/src/services/claudeAccountService.js +++ b/src/services/claudeAccountService.js @@ -3,8 +3,8 @@ const crypto = require('crypto') const ProxyHelper = require('../utils/proxyHelper') const axios = require('axios') const redis = require('../models/redis') -const logger = require('../utils/logger') const config = require('../../config/config') +const logger = require('../utils/logger') const { maskToken } = require('../utils/tokenMask') const { logRefreshStart, @@ -723,7 +723,9 @@ class ClaudeAccountService { // 如果有会话哈希,建立新的映射 if (sessionHash) { - await redis.setSessionAccountMapping(sessionHash, selectedAccountId, 3600) // 1小时过期 + // 从配置获取TTL(小时),转换为秒 + const ttlSeconds = (config.session?.stickyTtlHours || 1) * 60 * 60 + await redis.setSessionAccountMapping(sessionHash, selectedAccountId, ttlSeconds) logger.info( `🎯 Created new sticky session mapping: ${sortedAccounts[0].name} (${selectedAccountId}) for session ${sessionHash}` ) @@ -877,7 +879,9 @@ class ClaudeAccountService { // 如果有会话哈希,建立新的映射 if (sessionHash) { - await redis.setSessionAccountMapping(sessionHash, selectedAccountId, 3600) // 1小时过期 + // 从配置获取TTL(小时),转换为秒 + const ttlSeconds = (config.session?.stickyTtlHours || 1) * 60 * 60 + await redis.setSessionAccountMapping(sessionHash, selectedAccountId, ttlSeconds) logger.info( `🎯 Created new sticky session mapping for shared account: ${candidateAccounts[0].name} (${selectedAccountId}) for session ${sessionHash}` )