gongxiangp2/lib/image-processing-limiter.ts
2026-08-02 01:04:11 +08:00

50 lines
1.5 KiB
TypeScript

const DEFAULT_MAX_CONCURRENT_IMAGE_JOBS = 1
const MAX_ALLOWED_IMAGE_JOBS = 2
function readMaxConcurrentImageJobs(): number {
const configured = Number.parseInt(process.env.IMAGE_PROCESSING_MAX_CONCURRENT || '', 10)
if (!Number.isFinite(configured)) {
return DEFAULT_MAX_CONCURRENT_IMAGE_JOBS
}
return Math.max(1, Math.min(MAX_ALLOWED_IMAGE_JOBS, configured))
}
const maxConcurrentImageJobs = readMaxConcurrentImageJobs()
let activeImageJobs = 0
const waitingImageJobs: Array<() => void> = []
async function acquireImageProcessingSlot(): Promise<void> {
if (activeImageJobs < maxConcurrentImageJobs) {
activeImageJobs += 1
return
}
await new Promise<void>((resolve) => waitingImageJobs.push(resolve))
}
function releaseImageProcessingSlot(): void {
const next = waitingImageJobs.shift()
if (next) {
// 将当前占用的名额直接交给队首等待者。这里不能先减 active 再让等待者加回,
// 否则两者之间的新请求可能抢到这个瞬时空位,突破配置的并发上限。
next()
return
}
activeImageJobs = Math.max(0, activeImageJobs - 1)
}
/**
* 跨请求限制 Sharp 任务数。单个请求内串行并不能阻止多个请求同时解码大图,
* 所以白底图、预览图和长图处理都必须共用这个进程级门闩。
*/
export async function withImageProcessingSlot<T>(task: () => Promise<T>): Promise<T> {
await acquireImageProcessingSlot()
try {
return await task()
} finally {
releaseImageProcessingSlot()
}
}