mirror of
https://github.com/2234839/web-font.git
synced 2026-09-04 14:53:32 +08:00
fix: 并发默认调到4,队列超时改为可配(30s)
- SUBSET_CONCURRENCY 默认 2 → 4(900M 内存够用) - 新增 SUBSET_QUEUE_TIMEOUT 环境变量(默认30秒,原硬编码10秒) - 压测发现10秒超时太短:50并发只有8成功,其余排队超时
This commit is contained in:
parent
65ba984972
commit
b86d2fcb53
@ -125,8 +125,8 @@ services:
|
||||
- SUBSET_CACHE_MAX_SIZE=10485760
|
||||
# Temp font retention (seconds), auto-deleted if unused. Default: 10800 (3h)
|
||||
- TEMP_RETENTION_SECONDS=10800
|
||||
# Max concurrent font subsetting. Default: 2
|
||||
- SUBSET_CONCURRENCY=2
|
||||
# Max concurrent font subsetting. Default: 4
|
||||
- SUBSET_CONCURRENCY=4
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
@ -125,8 +125,8 @@ services:
|
||||
- SUBSET_CACHE_MAX_SIZE=10485760
|
||||
# 临时字体保留时限(秒),超时未使用自动删除,默认 10800(3小时)
|
||||
- TEMP_RETENTION_SECONDS=10800
|
||||
# 字体裁剪最大并发数,默认 2(内存受限环境建议 1-3)
|
||||
- SUBSET_CONCURRENCY=2
|
||||
# 字体裁剪最大并发数,默认 4(内存受限环境建议 2-3)
|
||||
- SUBSET_CONCURRENCY=4
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
@ -25,10 +25,18 @@ export const subsetCacheMaxSize = parseInt(env.SUBSET_CACHE_MAX_SIZE ?? `${10 *
|
||||
* 字体子集化最大并发数
|
||||
*
|
||||
* 字体裁剪是 CPU/内存密集操作,并发过多会导致 LLRT OOM 崩溃。
|
||||
* 默认 2:在 900M 内存限制下安全运行。
|
||||
* 服务器内存充足时可适当调大(如 4-8)。
|
||||
* 默认 4:在 900M 内存限制下安全运行。
|
||||
* 内存充裕可调大,内存紧张可调小到 2。
|
||||
*/
|
||||
export const subsetConcurrency = Math.max(1, parseInt(env.SUBSET_CONCURRENCY ?? "2", 10) || 2);
|
||||
export const subsetConcurrency = Math.max(1, parseInt(env.SUBSET_CONCURRENCY ?? "4", 10) || 4);
|
||||
|
||||
/**
|
||||
* 队列等待超时(秒)—— 排队超过此时间返回 503,避免请求无限堆积
|
||||
*
|
||||
* 50 并发场景下并发 4,每批 ~300ms,排队最久约 (50/4)*300ms ≈ 3.75s,
|
||||
* 30 秒足够覆盖正常高峰。极端过载时快速失败让客户端重试。
|
||||
*/
|
||||
export const subsetQueueTimeoutSeconds = Math.max(5, parseInt(env.SUBSET_QUEUE_TIMEOUT ?? "30", 10) || 30);
|
||||
|
||||
/** 字体搜索目录(按优先级排序:admin > 普通 > 临时) */
|
||||
export const fontDirs = ["font/admin", "font", "font/temp"] as const;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { jsonResponse } from "../shared";
|
||||
import { enableTempUpload, adminApiKey, tempRetentionSeconds, subsetConcurrency } from "../config";
|
||||
import { enableTempUpload, adminApiKey, tempRetentionSeconds, subsetConcurrency, subsetQueueTimeoutSeconds } from "../config";
|
||||
|
||||
/** GET /api/config — 返回公开配置 */
|
||||
export async function handleGetConfig(req: Request, _res: Response) {
|
||||
@ -13,6 +13,8 @@ export async function handleGetConfig(req: Request, _res: Response) {
|
||||
tempRetentionSeconds,
|
||||
/** 字体子集化最大并发数 */
|
||||
subsetConcurrency,
|
||||
/** 队列等待超时(秒) */
|
||||
subsetQueueTimeoutSeconds,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import type { FontEditor } from "../../vendor/fonteditor-core/lib/ttf/font.js";
|
||||
import { parseUrl, stats, subsetCache, findFontPath, readFontBuffer, markStatsDirty } from "../shared";
|
||||
import { markFontUsed } from "../temp_cleaner";
|
||||
import { withConcurrencyLimit } from "../subset_queue";
|
||||
import { subsetConcurrency } from "../config";
|
||||
import { subsetConcurrency, subsetQueueTimeoutSeconds } from "../config";
|
||||
|
||||
/**
|
||||
* 进程启动时戳(模块加载时取一次,进程重启即变化)
|
||||
@ -121,7 +121,7 @@ export async function handleFontSubset(req: Request, res: Response) {
|
||||
outType: outType,
|
||||
sourceType: fontType,
|
||||
});
|
||||
});
|
||||
}, subsetQueueTimeoutSeconds * 1000);
|
||||
|
||||
/** 排队超时,返回 503 让客户端重试 */
|
||||
if (subsetResult === null) {
|
||||
|
||||
@ -8,8 +8,11 @@
|
||||
* 等待超时则返回 503,避免请求无限堆积。
|
||||
*/
|
||||
|
||||
/** 等待队列中的请求超时时间(毫秒),默认 10 秒 */
|
||||
const QUEUE_TIMEOUT_MS = 10_000;
|
||||
/**
|
||||
* 等待队列中的请求超时时间(秒),由 config.ts 通过环境变量控制
|
||||
*
|
||||
* 在 subset.ts 中通过参数传入,不在此处硬编码。
|
||||
*/
|
||||
|
||||
/** 当前正在执行的子集化数量 */
|
||||
let activeCount = 0;
|
||||
@ -25,11 +28,13 @@ let waitingCount = 0;
|
||||
*
|
||||
* @param maxConcurrency 最大并发数
|
||||
* @param task 实际的子集化异步任务
|
||||
* @param queueTimeoutMs 排队等待超时(毫秒),超时返回 null
|
||||
* @returns 任务结果,或 null 表示排队超时
|
||||
*/
|
||||
export async function withConcurrencyLimit<T>(
|
||||
maxConcurrency: number,
|
||||
task: () => Promise<T>,
|
||||
queueTimeoutMs: number,
|
||||
): Promise<T | null> {
|
||||
/** 并发未满,直接执行 */
|
||||
if (activeCount < maxConcurrency) {
|
||||
@ -47,7 +52,7 @@ export async function withConcurrencyLimit<T>(
|
||||
waitingCount++;
|
||||
try {
|
||||
/** 等待获取许可,或超时 */
|
||||
const acquired = await waitForSlot(QUEUE_TIMEOUT_MS);
|
||||
const acquired = await waitForSlot(queueTimeoutMs);
|
||||
if (!acquired) {
|
||||
/** 排队超时,返回 null 让调用方返回 503 */
|
||||
return null;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user