修复整站502(健康探针被慢请求饿死)+全部模特死链+图片并发限流
- 后端 gunicorn 加 --threads 4:纯 sync worker 下几张图就占满 3 worker, 健康探针 /api/health/ 拿不到 worker→超时→pod 判不就绪→流量被掐→整站502。 图片生成 I/O 密集,加线程后慢请求不再饿死探针/其他接口。 - 前端图片生成并发上限 2:每张仍独立短请求,但不再 N 条一起砸单 pod 致 OOM。 - ai-tools 全部模特→ 接成展开/收起全部模特(原为纯 span 死链,模特>6 时第7个起选不到)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -355,17 +355,28 @@ export function App() {
|
||||
}
|
||||
|
||||
function generateImages(payload: { prompt: string; mode?: "image" | "model" | "cover"; count?: number }) {
|
||||
// 每张图一条独立 HTTP 请求、并行发出(不再让一条请求串行出 N 张)——
|
||||
// 单张约 30s 不会触发后端请求超时;并行后 N 张总耗时≈单张,也不长时间占用后端进程。
|
||||
// 每张图一条独立 HTTP 请求(不再让一条请求串行出 N 张)——单张约 30s 不会触发后端请求超时。
|
||||
// 但不全量并行:同时最多 CONCURRENCY 条,避免 N 条一起砸向单个后端 pod 致内存峰值翻倍 OOM→502。
|
||||
// 任一张失败只丢那一张(各自独立扣费/回滚),其余正常返回。
|
||||
const n = Math.max(1, Math.min(Number(payload.count) || 1, 12));
|
||||
const CONCURRENCY = 2;
|
||||
return action(async () => {
|
||||
const settled = await Promise.allSettled(
|
||||
Array.from({ length: n }, () => api.generateImage({ prompt: payload.prompt, mode: payload.mode, count: 1 }))
|
||||
);
|
||||
const assets = settled.flatMap((r) => (r.status === "fulfilled" ? r.value.assets : []));
|
||||
const results: Array<{ ok: true; assets: Asset[] } | { ok: false; reason: unknown }> = new Array(n);
|
||||
let next = 0;
|
||||
async function worker() {
|
||||
for (let i = next++; i < n; i = next++) {
|
||||
try {
|
||||
const res = await api.generateImage({ prompt: payload.prompt, mode: payload.mode, count: 1 });
|
||||
results[i] = { ok: true, assets: res.assets };
|
||||
} catch (reason) {
|
||||
results[i] = { ok: false, reason };
|
||||
}
|
||||
}
|
||||
}
|
||||
await Promise.all(Array.from({ length: Math.min(CONCURRENCY, n) }, worker));
|
||||
const assets = results.flatMap((r) => (r.ok ? r.assets : []));
|
||||
if (assets.length === 0) {
|
||||
const firstErr = settled.find((r) => r.status === "rejected") as PromiseRejectedResult | undefined;
|
||||
const firstErr = results.find((r) => !r.ok) as { ok: false; reason: unknown } | undefined;
|
||||
throw new Error(firstErr ? String((firstErr.reason as Error)?.message || firstErr.reason || "生成失败") : "未生成任何图片");
|
||||
}
|
||||
return { assets };
|
||||
|
||||
Reference in New Issue
Block a user