feat: add async image task API
This commit is contained in:
+78
-1
@@ -11,9 +11,11 @@
|
||||
-> 宝塔 / Nginx
|
||||
/static/ -> STATIC_ROOT
|
||||
/media/ -> MEDIA_ROOT(MVP 本地媒体;后续可换对象存储)
|
||||
/api/v1/generate/* -> Gunicorn 长请求池(图片同步,超时更长)
|
||||
/api/v1/generate/image/tasks* -> Gunicorn 普通请求池(异步提交/轮询,短请求)
|
||||
/api/v1/generate/* -> Gunicorn 长请求池(旧同步生成,超时更长)
|
||||
其他路径 -> Gunicorn 普通请求池(用户端 / admin / 余额 / 充值)
|
||||
-> cmhub Django 单体
|
||||
-> cmhub image-task worker(management command,后台处理异步生图)
|
||||
-> MySQL 8.4
|
||||
-> Django shared cache(MVP 可用 MySQL DatabaseCache;高并发换 Redis/Memcached)
|
||||
```
|
||||
@@ -112,6 +114,12 @@ STATIC_URL=/static/
|
||||
STATIC_ROOT=/www/wwwroot/cmhub/staticfiles
|
||||
MEDIA_ROOT=/www/wwwroot/cmhub/media
|
||||
MEDIA_URL=/media/
|
||||
PUBLIC_BASE_URL=https://cmhub.example.com
|
||||
MEDIA_PUBLIC_BASE_URL=https://cmhub.example.com
|
||||
IMAGE_TASK_RETENTION_HOURS=24
|
||||
GENERATED_IMAGE_RETENTION_HOURS=72
|
||||
IMAGE_TASK_REAPER_INTERVAL_SECONDS=60
|
||||
IMAGE_TASK_LEASE_SECONDS=600
|
||||
|
||||
DJANGO_CACHE_BACKEND=django.core.cache.backends.db.DatabaseCache
|
||||
DJANGO_CACHE_LOCATION=cmhub_cache
|
||||
@@ -136,6 +144,7 @@ python3.12 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key
|
||||
- 当前注册策略为免邮箱验证,注册登录不依赖邮件服务;T-608 后注册成功会赠送 100 点,生产必须保留或收紧 `ACCOUNT_SIGNUP_RATE_LIMIT`,并使用共享 Django cache 承载限流计数;若后续启用密码找回、通知或恢复邮箱验证,再把 `DJANGO_EMAIL_BACKEND` 改为真实 SMTP / 邮件服务并配置 `DJANGO_DEFAULT_FROM_EMAIL`。
|
||||
- `PAYMENT_CALLBACK_MODE=sdk` 必须配齐微信 / 支付宝商户配置;未配齐时先保持 `mock`。
|
||||
- 宝塔 / Nginx 已强制 HTTPS 时,`DJANGO_SECURE_SSL_REDIRECT=false` 即可;全站 HTTPS 稳定后再把 `DJANGO_SECURE_HSTS_SECONDS` 调大,避免 HSTS 误锁域名。
|
||||
- T-614 异步生图 worker 没有 request 对象,生产必须配置 `MEDIA_PUBLIC_BASE_URL` 或 `PUBLIC_BASE_URL` 为公开 HTTPS 域名,否则异步轮询成功时可能返回相对 `/media/...` URL。
|
||||
|
||||
## 五、初始化数据库与静态文件
|
||||
|
||||
@@ -262,6 +271,42 @@ gunicorn config.wsgi:application \
|
||||
|
||||
systemd 单元可分别命名为 `cmhub-web.service` 和 `cmhub-generate.service`。服务的 `WorkingDirectory` 指向 `/www/wwwroot/cmhub`,`ExecStart` 使用上面的两条 Gunicorn 命令,环境变量由项目根目录 `.env` 在 Django settings 中读取。
|
||||
|
||||
异步生图 worker 示例:
|
||||
|
||||
```bash
|
||||
python3.12 manage.py run_image_tasks \
|
||||
--worker-id cmhub-image-worker-1 \
|
||||
--sleep-seconds 1
|
||||
```
|
||||
|
||||
建议单独托管为 `cmhub-image-worker.service`。该 worker 从数据库 `image_generation_task` 表抢 `queued` 任务,使用 MySQL `select_for_update(skip_locked)` 标记 `running`,执行成功后写 `succeeded` 和稳定 `result_url`;失败或上游超时会调用计费层退点并写 `failed`。worker 循环会按 `IMAGE_TASK_REAPER_INTERVAL_SECONDS` 扫描租约或心跳过期的 `running` 任务,默认判失败并幂等退点,不默认重排队。
|
||||
|
||||
systemd 单元示例:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=cmhub image task worker
|
||||
After=network.target mysql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/www/wwwroot/cmhub
|
||||
ExecStart=/usr/bin/python3.12 manage.py run_image_tasks --worker-id cmhub-image-worker-1 --sleep-seconds 1
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
User=www
|
||||
Group=www
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
部署异步生图时至少需要同时运行:
|
||||
|
||||
- `cmhub-web.service`:用户端、admin、余额、模型目录、异步提交 / 轮询等短请求。
|
||||
- `cmhub-generate.service`:旧同步生成接口,保留给老客户端。
|
||||
- `cmhub-image-worker.service`:新异步任务真正调上游生成图片。
|
||||
|
||||
## 八、宝塔 / Nginx 配置
|
||||
|
||||
宝塔中新建站点并绑定域名和 SSL 后,在站点 Nginx 配置中加入或调整:
|
||||
@@ -285,6 +330,28 @@ server {
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
|
||||
location = /api/v1/generate/image/tasks {
|
||||
proxy_pass http://127.0.0.1:8001;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 120s;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
location ^~ /api/v1/generate/image/tasks/ {
|
||||
proxy_pass http://127.0.0.1:8001;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 120s;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
location /api/v1/generate/ {
|
||||
proxy_pass http://127.0.0.1:8002;
|
||||
proxy_set_header Host $host;
|
||||
@@ -313,6 +380,7 @@ server {
|
||||
|
||||
- `/static/` 必须指向 `STATIC_ROOT`,否则 Bootstrap/qrcode 本地资源在 `DEBUG=False` 下 404。
|
||||
- `/media/` 是 MVP 本地图片结果访问路径;如果改对象存储,应同步更新 `MEDIA_URL` 和存储配置。
|
||||
- `/api/v1/generate/image/tasks` 与 `/api/v1/generate/image/tasks/{task_id}` 必须写在 `/api/v1/generate/` 前面,确保异步提交 / 轮询走普通 web 池;旧同步生成接口继续走长请求池。
|
||||
- 宝塔若已在外层配置 HTTP 到 HTTPS 跳转,`DJANGO_SECURE_SSL_REDIRECT=false` 即可;如果由 Django 负责跳转,再设为 `true`。
|
||||
|
||||
## 九、上线前验证
|
||||
@@ -335,6 +403,13 @@ python3.12 manage.py test apps.api --noinput --keepdb --verbosity 2
|
||||
python3.12 manage.py test apps.portal --noinput --keepdb --verbosity 2
|
||||
```
|
||||
|
||||
异步生图 worker 单次验证:
|
||||
|
||||
```bash
|
||||
python3.12 manage.py run_image_tasks --once --worker-id smoke-worker
|
||||
python3.12 manage.py run_image_tasks --reap-only
|
||||
```
|
||||
|
||||
AI smoke:
|
||||
|
||||
```bash
|
||||
@@ -362,6 +437,8 @@ python3.12 manage.py smoke_ai_generation image
|
||||
- HTTPS 已由宝塔 / Nginx 强制跳转;如由 Django 强制跳转则设置 `DJANGO_SECURE_SSL_REDIRECT=true`。HSTS 只在确认全站 HTTPS 后启用。
|
||||
- `STATIC_ROOT` 已 `collectstatic`,Nginx 可访问 `/static/portal/vendor/bootstrap/bootstrap.min.css` 和 `/static/portal/vendor/qrcode/qrcode.js`。
|
||||
- `MEDIA_ROOT` 或对象存储可访问生成图片 URL。
|
||||
- `MEDIA_PUBLIC_BASE_URL` / `PUBLIC_BASE_URL` 已配置为客户端可访问的 HTTPS 域名;异步生图成功返回的 `result.image_url` 可公网下载。
|
||||
- `cmhub-image-worker.service` 已启动并设置开机自启;`journalctl -u cmhub-image-worker.service` 无持续异常。
|
||||
- `DJANGO_CACHE_BACKEND` 为共享 cache,不是 `LocMemCache`。
|
||||
- MySQL 是 8.4 / InnoDB / `utf8mb4`。
|
||||
- 真实支付 SDK 与商户配置齐全后,`PAYMENT_CALLBACK_MODE=sdk`。
|
||||
|
||||
Reference in New Issue
Block a user