mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 21:17:30 +00:00
feat(queue): 优化用户消息队列锁释放时机
将队列锁释放时机从"请求完成后"提前到"请求发送后",因为 Claude API 限流(RPM)基于请求发送时刻计算,无需等待响应完成。 主要变更: - 移除锁续租机制(startLockRenewal、refreshUserMessageLock) - 所有 relay 服务在请求发送成功后立即释放锁 - 流式请求通过 onResponseStart 回调在收到响应头时释放 - 调整默认配置:timeoutMs 60s→5s,lockTtlMs 120s→5s - 新增 USER_MESSAGE_QUEUE_LOCK_TTL_MS 环境变量支持
This commit is contained in:
@@ -14,9 +14,6 @@ const logger = require('../utils/logger')
|
||||
// 清理任务间隔
|
||||
const CLEANUP_INTERVAL_MS = 60000 // 1分钟
|
||||
|
||||
// 锁续租最大持续时间(从配置读取,与 REQUEST_TIMEOUT 保持一致)
|
||||
const MAX_RENEWAL_DURATION_MS = config.requestTimeout || 10 * 60 * 1000
|
||||
|
||||
// 轮询等待配置
|
||||
const POLL_INTERVAL_BASE_MS = 50 // 基础轮询间隔
|
||||
const POLL_INTERVAL_MAX_MS = 500 // 最大轮询间隔
|
||||
@@ -25,8 +22,6 @@ const POLL_BACKOFF_FACTOR = 1.5 // 退避因子
|
||||
class UserMessageQueueService {
|
||||
constructor() {
|
||||
this.cleanupTimer = null
|
||||
// 跟踪活跃的续租定时器,用于服务关闭时清理
|
||||
this.activeRenewalTimers = new Map()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,12 +69,13 @@ class UserMessageQueueService {
|
||||
*/
|
||||
async getConfig() {
|
||||
// 默认配置(防止 config.userMessageQueue 未定义)
|
||||
// 注意:优化后的默认值 - 锁持有时间从分钟级降到毫秒级,无需长等待
|
||||
const queueConfig = config.userMessageQueue || {}
|
||||
const defaults = {
|
||||
enabled: queueConfig.enabled ?? false,
|
||||
delayMs: queueConfig.delayMs ?? 100,
|
||||
timeoutMs: queueConfig.timeoutMs ?? 60000,
|
||||
lockTtlMs: queueConfig.lockTtlMs ?? 120000
|
||||
delayMs: queueConfig.delayMs ?? 200,
|
||||
timeoutMs: queueConfig.timeoutMs ?? 5000, // 从 60000 降到 5000,因为锁持有时间短
|
||||
lockTtlMs: queueConfig.lockTtlMs ?? 5000 // 从 120000 降到 5000,5秒足以覆盖请求发送
|
||||
}
|
||||
|
||||
// 尝试从 claudeRelayConfigService 获取 Web 界面配置
|
||||
@@ -100,7 +96,10 @@ class UserMessageQueueService {
|
||||
webConfig.userMessageQueueTimeoutMs !== undefined
|
||||
? webConfig.userMessageQueueTimeoutMs
|
||||
: defaults.timeoutMs,
|
||||
lockTtlMs: defaults.lockTtlMs
|
||||
lockTtlMs:
|
||||
webConfig.userMessageQueueLockTtlMs !== undefined
|
||||
? webConfig.userMessageQueueLockTtlMs
|
||||
: defaults.lockTtlMs
|
||||
}
|
||||
} catch {
|
||||
// 回退到环境变量配置
|
||||
@@ -232,83 +231,6 @@ class UserMessageQueueService {
|
||||
return released
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动锁续租(防止长连接超过TTL导致锁丢失)
|
||||
* @param {string} accountId - 账户ID
|
||||
* @param {string} requestId - 请求ID
|
||||
* @returns {Promise<Function>} 停止续租的函数
|
||||
*/
|
||||
async startLockRenewal(accountId, requestId) {
|
||||
const cfg = await this.getConfig()
|
||||
if (!cfg.enabled || !accountId || !requestId) {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
const intervalMs = Math.max(10000, Math.floor(cfg.lockTtlMs / 2)) // 约一半TTL刷新一次
|
||||
const maxRenewals = Math.ceil(MAX_RENEWAL_DURATION_MS / intervalMs) // 最大续租次数
|
||||
const startTime = Date.now()
|
||||
const timerKey = `${accountId}:${requestId}`
|
||||
|
||||
let stopped = false
|
||||
let renewalCount = 0
|
||||
|
||||
const stopRenewal = () => {
|
||||
if (!stopped) {
|
||||
clearInterval(timer)
|
||||
stopped = true
|
||||
this.activeRenewalTimers.delete(timerKey)
|
||||
}
|
||||
}
|
||||
|
||||
const timer = setInterval(async () => {
|
||||
if (stopped) {
|
||||
return
|
||||
}
|
||||
|
||||
renewalCount++
|
||||
|
||||
// 检查是否超过最大续租次数或最大持续时间
|
||||
if (renewalCount > maxRenewals || Date.now() - startTime > MAX_RENEWAL_DURATION_MS) {
|
||||
logger.warn(`📬 User message queue: max renewal duration exceeded, stopping renewal`, {
|
||||
accountId,
|
||||
requestId,
|
||||
renewalCount,
|
||||
durationMs: Date.now() - startTime
|
||||
})
|
||||
stopRenewal()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const refreshed = await redis.refreshUserMessageLock(accountId, requestId, cfg.lockTtlMs)
|
||||
if (!refreshed) {
|
||||
// 锁可能已被释放或超时,停止续租
|
||||
logger.warn(
|
||||
`📬 User message queue: failed to refresh lock (possibly lost), stop renewal`,
|
||||
{
|
||||
accountId,
|
||||
requestId,
|
||||
renewalCount
|
||||
}
|
||||
)
|
||||
stopRenewal()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('📬 User message queue: lock renewal error:', error)
|
||||
}
|
||||
}, intervalMs)
|
||||
|
||||
// 避免阻止进程退出
|
||||
if (typeof timer.unref === 'function') {
|
||||
timer.unref()
|
||||
}
|
||||
|
||||
// 跟踪活跃的定时器
|
||||
this.activeRenewalTimers.set(timerKey, { timer, stopRenewal, accountId, requestId, startTime })
|
||||
|
||||
return stopRenewal
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取队列统计信息
|
||||
* @param {string} accountId - 账户ID
|
||||
@@ -385,32 +307,6 @@ class UserMessageQueueService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止所有活跃的锁续租定时器(服务关闭时调用)
|
||||
*/
|
||||
stopAllRenewalTimers() {
|
||||
const count = this.activeRenewalTimers.size
|
||||
if (count > 0) {
|
||||
for (const [key, { stopRenewal }] of this.activeRenewalTimers) {
|
||||
try {
|
||||
stopRenewal()
|
||||
} catch (error) {
|
||||
logger.error(`📬 User message queue: failed to stop renewal timer ${key}:`, error)
|
||||
}
|
||||
}
|
||||
this.activeRenewalTimers.clear()
|
||||
logger.info(`📬 User message queue: stopped ${count} active renewal timer(s)`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃续租定时器数量(用于监控)
|
||||
* @returns {number}
|
||||
*/
|
||||
getActiveRenewalCount() {
|
||||
return this.activeRenewalTimers.size
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理孤儿锁
|
||||
* 检测异常情况:锁存在但没有设置过期时间(lockTtlRaw === -1)
|
||||
|
||||
Reference in New Issue
Block a user