定位
pf-compose-service 是图模工坊的独立图片合成微服务——把 sceneJson 用 Puppeteer 无头浏览器渲染成图片,再自行上传 MinIO,只回传 URL。它从 pf-service 解耦而来(2809bf4),让主服务不必背 Chromium、可轻量水平扩展。
- 运行时:Bun(
import.meta.main入口) - HTTP:裸
Bun.serve(无 Elysia,手写路由if链) - 渲染:Puppeteer
^24.4.0+ Leafer / leafer-ui2.2.9 - 条码/二维码:
@sa/code-renderer|日志:winston + daily-rotate - 端口:
3030(生产 Docker 内同3030,宿主映射3031)
源码结构
1 | apps/pf-compose-service/ |
HTTP 接口
所有 JSON 响应统一 { code, message, data, timestamp }(src/utils/http.ts),成功码 "0000",CORS *。
| 方法/路径 | 说明 |
|---|---|
GET / |
存活信息 |
GET /health |
健康 + Worker 池统计 |
POST /api/compose |
合成入口(注意是 /api/compose) |
GET|HEAD /storage/* |
S3 代理,流式回传 MinIO 对象 |
GET /renderer/* |
内嵌 Leafer 渲染页/打包产物 |
GET|HEAD /img/*、/editor/img/* |
public/ 静态图 |
POST /api/compose
1 | // 请求 |
- 渲染产物由本服务自行
PutObject入库 MinIO(ossStorageService.uploadBufferToS3,BunS3Client),对象键[tenant-<id>/]<purpose>/<yyyy/mm/dd>/<baseName>.<ext>。 publicUrl由config.s3.publicUrl(/storage)拼出——成品图仍通过本服务的GET /storage/*代理回读。- 合成失败以 HTTP 200 +
code="5001"返回(调用方按 code 判成败)。
Worker 进程池
核心在 src/utils/puppeteerPagePool.ts。它不是 Bun Worker 线程,也不是子进程,而是一池独立的完整 Chromium 浏览器实例(Puppeteer 启动),每个实例独占一个 Page:
BrowserWorker(别名PageWorker):持有一个Browser+ 一个Page+ 独立userDataDir,跟踪usesCount / maxUses / isHealthy / isBusy。render(sceneJson, options)调页内window.renderScene(json, {pixelRatio, format}),与timeoutMs(默认 30000)竞速,拿到data:image/...base64 后解码为Buffer返回;出错即标记不健康。PuppeteerBrowserPool(别名PuppeteerPagePool):维护idleWorkers[]、activeWorkers、pendingWaitersFIFO 队列。warmUp()并行预热到workers个(默认 3)acquire(timeoutMs=60000):复用空闲 → 未达上限则新建 → 否则排队(超时 reject)release()归还或交给等待者,低于目标时触发再预热withWorker(fn)acquire/release 包裹(抛错标不健康)- 每 Worker 一个唯一
userDataDir(<cwd>/.runtime/puppeteer/worker-...),视口 1920×1080,导航到renderUrl(networkidle0)后等待window.renderScene与window.__LEAFER_READY__ === true
src/utils/puppeteerLauncher.ts 负责 headless 启动参数(--no-sandbox、--disable-dev-shm-usage、--disable-gpu 等),并有相当完善的僵尸进程恢复:按 service-<port>-<workerId> 的运行锁、browser-state.json 记录 pid/wsEndpoint/userDataDir、杀进程前校验命令行、隔离不安全状态。
内嵌 Leafer 渲染页
public/renderer/index.html 极简(一个全屏 #leafer-view + runner.js);runner.js 是 src/renderer/runner.ts 的 Bun 打包产物(IIFE、browser target),在页内建一个 preview 模式的 Leafer App,暴露 window.renderScene / resetScene / initData / app / __LEAFER_READY__。
renderScene 的处理:把 data.children(或 [data])当作 Leafer IUIInputData[],重置画布 → 预加载所有图片 URL(collectImageUrls 递归 fill/url/children)→ 注入节点树 → 用 @sa/code-renderer 刷新条码/二维码节点 → 等字体/图片/视图完成 → app.export(format, {pixelRatio}) 返回 data URL。
配置(config.json)
1 | { |
CONFIG_PATH / PF_COMPOSE_SERVICE_PORT 环境变量可覆盖;puppeteer.renderUrl 为可选键(默认 http://127.0.0.1:<port>/renderer/index.html)。
⚠️ S3 凭据在 config.json 内明文提交,属本地/开发默认值,生产请用配置注入覆盖。
健康检查
1 | GET /health → |
pf-service 侧 checkComposeHealth() 以 code==="0000" && data.status==="ready" 判就绪。
开发与部署
1 | bun dev:compose # 根目录:bun run --filter @wangijun/pf-compose-service dev |
生产镜像基于 ghcr.io/puppeteer/puppeteer:24.4.0,额外装中日韩字体(noto-cjk / wqy-zenhei / wqy-microhei)并指向系统 Chrome,详见 部署。