From 9c3e4e5c8b14f06f4a953879b4cd6aca0fe02ada Mon Sep 17 00:00:00 2001 From: zyc <1439655764@qq.com> Date: Mon, 15 Jun 2026 15:38:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=BE=E7=89=87=E7=94=9F=E6=88=90=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E6=AF=8F=E5=BC=A0=E4=B8=80=E6=9D=A1=E5=B9=B6=E8=A1=8C?= =?UTF-8?q?=E8=AF=B7=E6=B1=82,=E6=A0=B9=E6=B2=BB=E5=A4=9A=E5=9B=BE?= =?UTF-8?q?=E7=94=9F=E6=88=90=E8=B6=85=E6=97=B6=20500?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:多图生成走一条 HTTP 串行出 N 张,4 张累计 >120s 被 gunicorn 强杀 worker → 500 (日志实锤 SystemExit on ARK response.begin)。采纳用户建议拆成「每张一条请求」: - App.tsx generateImages: 拆成 N 条并行单张请求(Promise.allSettled),单张约 30s 不超时, 并行后总耗时≈单张(比串行快 N 倍),任一张失败只丢那张(各自独立扣费/回滚) - Dockerfile: gunicorn --timeout 120→300,给慢 ARK 留余量(双保险) Co-Authored-By: Claude Opus 4.8 --- core/backend/Dockerfile | 4 +++- core/frontend/src/App.tsx | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/core/backend/Dockerfile b/core/backend/Dockerfile index daa424e..bb99c8a 100644 --- a/core/backend/Dockerfile +++ b/core/backend/Dockerfile @@ -32,4 +32,6 @@ RUN chmod +x /usr/local/bin/docker-entrypoint.sh EXPOSE 8000 ENTRYPOINT ["docker-entrypoint.sh"] -CMD ["gunicorn", "airshelf.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3", "--timeout", "120"] +# timeout 300:前端已把多图生成拆成「每张一条请求」,单张约 30s;300s 给慢 ARK 留充足余量, +# 不再因一条请求串行出多张而超时(旧 120s 下生成 4 张必被 worker 强杀 → 500)。 +CMD ["gunicorn", "airshelf.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3", "--timeout", "300"] diff --git a/core/frontend/src/App.tsx b/core/frontend/src/App.tsx index 8796dc5..6b0049f 100644 --- a/core/frontend/src/App.tsx +++ b/core/frontend/src/App.tsx @@ -355,7 +355,21 @@ export function App() { } function generateImages(payload: { prompt: string; mode?: "image" | "model" | "cover"; count?: number }) { - return action(() => api.generateImage(payload), "图片已生成"); + // 每张图一条独立 HTTP 请求、并行发出(不再让一条请求串行出 N 张)—— + // 单张约 30s 不会触发后端请求超时;并行后 N 张总耗时≈单张,也不长时间占用后端进程。 + // 任一张失败只丢那一张(各自独立扣费/回滚),其余正常返回。 + const n = Math.max(1, Math.min(Number(payload.count) || 1, 12)); + 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 : [])); + if (assets.length === 0) { + const firstErr = settled.find((r) => r.status === "rejected") as PromiseRejectedResult | undefined; + throw new Error(firstErr ? String((firstErr.reason as Error)?.message || firstErr.reason || "生成失败") : "未生成任何图片"); + } + return { assets }; + }, "图片已生成"); } function onAuthed(payload: { token: string; user: User; team: Team }) {