feat(concurrencyManagement): implement concurrency status management API and enhance concurrency handling in middleware

This commit is contained in:
DaydreamCoding
2025-12-06 17:23:42 +08:00
committed by QTom
parent d81a16b98d
commit f74f77ef65
4 changed files with 430 additions and 2 deletions

View File

@@ -226,8 +226,18 @@ const authenticateApiKey = async (req, res, next) => {
)
if (currentConcurrency > concurrencyLimit) {
// 如果超过限制,立即减少计数
await redis.decrConcurrency(validation.keyData.id, requestId)
// 如果超过限制,立即减少计数(添加 try-catch 防止异常导致并发泄漏)
try {
const newCount = await redis.decrConcurrency(validation.keyData.id, requestId)
logger.api(
`📉 Decremented concurrency (429 rejected) for key: ${validation.keyData.id} (${validation.keyData.name}), new count: ${newCount}`
)
} catch (error) {
logger.error(
`Failed to decrement concurrency after limit exceeded for key ${validation.keyData.id}:`,
error
)
}
logger.security(
`🚦 Concurrency limit exceeded for key: ${validation.keyData.id} (${
validation.keyData.name
@@ -249,7 +259,38 @@ const authenticateApiKey = async (req, res, next) => {
let leaseRenewInterval = null
if (renewIntervalMs > 0) {
// 🔴 关键修复:添加最大刷新次数限制,防止租约永不过期
// 默认最大生存时间为 10 分钟,可通过环境变量配置
const maxLifetimeMinutes = parseInt(process.env.CONCURRENCY_MAX_LIFETIME_MINUTES) || 10
const maxRefreshCount = Math.ceil((maxLifetimeMinutes * 60 * 1000) / renewIntervalMs)
let refreshCount = 0
leaseRenewInterval = setInterval(() => {
refreshCount++
// 超过最大刷新次数,强制停止并清理
if (refreshCount > maxRefreshCount) {
logger.warn(
`⚠️ Lease refresh exceeded max count (${maxRefreshCount}) for key ${validation.keyData.id} (${validation.keyData.name}), forcing cleanup after ${maxLifetimeMinutes} minutes`
)
// 清理定时器
if (leaseRenewInterval) {
clearInterval(leaseRenewInterval)
leaseRenewInterval = null
}
// 强制减少并发计数(如果还没减少)
if (!concurrencyDecremented) {
concurrencyDecremented = true
redis.decrConcurrency(validation.keyData.id, requestId).catch((error) => {
logger.error(
`Failed to decrement concurrency after max refresh for key ${validation.keyData.id}:`,
error
)
})
}
return
}
redis
.refreshConcurrencyLease(validation.keyData.id, requestId, leaseSeconds)
.catch((error) => {