跳到主要内容

BioF3 在线绘图服务 — 架构设计文档

本文档定义 BioF3 在线绘图工具(plot.biof3.com)的技术架构,作为下一阶段独立项目的启动参考。


1. 产品定位

维度描述
目标用户生物信息学研究者、研究生、临床生信分析师
核心价值上传数据 → 选择图类型 → 调参数 → 一键出 SCI 级图
所属项目OmicsHub(已有项目,单细胞云平台),绘图工具作为新增模块
与 BioF3 的关系FigCode 页面的"在线绘图"按钮跳转到 OmicsHub 绘图模块
访问方式http://<服务器IP>:<端口>/bindplot(OmicsHub 内的路由)

2. 功能规划

Phase 1(MVP)

  • 支持 10 种核心图类型(火山图、热图、PCA、富集 dotplot、生存曲线等)
  • 用户上传 CSV/TSV → 选择图类型 → 调参数 → 预览 → 下载 PNG/PDF
  • 无需注册即可使用(匿名)
  • 单次请求,无状态

Phase 2

  • 用户注册 + 历史记录
  • 更多图类型(30+)
  • 批量出图(一次上传多个文件)
  • 自定义配色方案保存

Phase 3

  • 流程性工具(差异分析 pipeline、富集分析 pipeline)
  • 多步骤 workflow(上传 → QC → 分析 → 出图)
  • API 接口(供第三方调用)
  • 团队协作功能

3. 技术架构

整体架构:前后端分离

┌─────────────────────────────────────────────────────────┐
│ 用户浏览器 │
│ plot.biof3.com (React / Next.js 前端) │
└────────────────────────┬────────────────────────────────┘
│ HTTPS API

┌─────────────────────────────────────────────────────────┐
│ API Gateway (Nginx) │
│ - 限流 / 认证 / CORS / 文件大小限制 │
└────────────┬───────────────────────────────┬────────────┘
│ │
▼ ▼
┌────────────────────────┐ ┌──────────────────────────┐
│ 计算服务 (R Worker) │ │ 计算服务 (Python Worker) │
│ - Plumber API │ │ - FastAPI │
│ - ggplot2 / pheatmap │ │ - matplotlib / seaborn │
│ - 容器化 (Docker) │ │ - 容器化 (Docker) │
└────────────────────────┘ └──────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────┐
│ 共享存储 / 缓存 │
│ - Redis(任务队列 + 结果缓存) │
│ - MinIO / OSS(文件存储:上传数据 + 生成图片) │
└─────────────────────────────────────────────────────────┘

前端

选择理由
Next.js 14+ (App Router)SSR/SSG 混合、API Routes 可做 BFF、React 生态
TypeScript类型安全
Tailwind CSS快速 UI 开发,和 BioF3 配色统一
Zustand轻量状态管理
React Query异步请求管理 + 缓存

后端(API 层 + 计算层分离)

核心思路:API 层用专业后端框架,R 只作为计算引擎被调用。

前端 → API 层(Node.js / Go / Python)→ 任务队列 → R 执行器(子进程)

API 层(选一)

方案语言优势适用
NestJSTypeScript和前端同语言、装饰器路由、模块化、生态丰富团队熟 TS
Go (Gin / Fiber)Go性能极高、并发原生、二进制部署简单追求性能
FastAPIPython自动文档、类型校验、async 原生团队熟 Python

推荐:NestJS(和 Next.js 前端同语言栈,减少上下文切换;TypeORM 做数据库;Bull 做任务队列都是 Node 生态原生)

R 执行器(计算层)

R 不再暴露 HTTP 接口,而是作为子进程被 API 层调用:

API 收到请求 → 参数校验 → 写入临时 JSON 参数文件
→ spawn("Rscript", ["plots/volcano.R", "--params", "/tmp/xxx.json"])
→ R 读参数 → ggplot2 出图 → 写 PNG/PDF 到指定路径
→ API 读取结果文件 → 返回给前端

或者用 队列模式(高并发时):

API → Redis Bull Queue → Worker 进程 → Rscript 子进程 → 结果回写

这样 R 的职责非常纯粹:

  • 接收一个 JSON 参数文件
  • 读取用户数据文件
  • 执行 ggplot2 代码
  • 输出图片到指定路径
  • 退出

不需要 Plumber、不需要 R 做 HTTP、不需要 R 处理认证/限流/错误。

架构图(更新版)

┌─────────────────────────────────────────────────────────┐
│ 用户浏览器 │
│ plot.biof3.com (Next.js 前端) │
└────────────────────────┬────────────────────────────────┘
│ HTTPS

┌─────────────────────────────────────────────────────────┐
│ API 服务 (NestJS / Go / FastAPI) │
│ - 路由 / 认证 / 限流 / 参数校验 / 文件管理 │
│ - 任务调度(Bull Queue → Redis) │
│ - 结果轮询 / WebSocket 推送 │
└────────────────────────┬────────────────────────────────┘
│ 任务队列

┌─────────────────────────────────────────────────────────┐
│ Worker 进程池 │
│ - 从队列取任务 │
│ - spawn Rscript 子进程执行绘图 │
│ - 超时 kill / 错误捕获 / 结果上传 │
│ - 每个 Worker 可并行跑多个 R 子进程(受 CPU 限制) │
└────────────────────────┬────────────────────────────────┘

┌──────────┴──────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ R 执行环境 │ │ Python 执行环境 │
│ (Docker 容器) │ │ (Docker 容器,备选) │
│ - ggplot2 │ │ - matplotlib │
│ - pheatmap │ │ - seaborn │
│ - 只读数据文件 │ │ - 只读数据文件 │
│ - 只写输出目录 │ │ - 只写输出目录 │
└──────────────────┘ └──────────────────────┘

R 脚本的标准接口

每个图类型对应一个 R 脚本,统一接口:

#!/usr/bin/env Rscript
# plots/volcano.R
# 标准接口:读 JSON 参数 → 出图 → 退出

library(jsonlite)
library(ggplot2)
library(ggrepel)

# 1. 读取参数
args <- commandArgs(trailingOnly = TRUE)
params <- fromJSON(args[1])

# 2. 读取用户数据
data <- read.csv(params$data_path)

# 3. 绑定代码(参数化)
p <- ggplot(data, aes(x = .data[[params$x_col]],
y = -log10(.data[[params$p_col]]),
color = sig)) +
geom_point(size = params$point_size, alpha = params$alpha) +
# ... 其余参数化代码

# 4. 输出
ggsave(params$output_path, p, width = params$width, height = params$height, dpi = 300)

# 5. 返回状态
cat(toJSON(list(status = "success", output = params$output_path)))

API 层只需要:

// NestJS Worker
const result = await execRscript('plots/volcano.R', paramsJsonPath);
// result = { status: "success", output: "/tmp/xxx/volcano.png" }

基础设施

组件选择说明
容器编排Docker Compose(初期)→ K8s(后期)初期简单,后期按需扩展
反向代理Nginx限流、SSL、静态文件
消息队列Redis任务队列 + 结果缓存
文件存储阿里云 OSSMinIO用户上传文件 + 生成图片
数据库PostgreSQL用户、历史记录、配置
监控Prometheus + Grafana请求量、响应时间、错误率

4. 核心流程

单次绘图请求

1. 用户在前端选择图类型(如 Volcano Plot)
2. 上传数据文件(CSV/TSV,< 50MB)
3. 前端校验格式(列名、数据类型)
4. 调整参数(阈值、颜色、标注数量等)
5. 点击"生成"→ POST /api/plot/volcano
6. API Gateway 验证 + 限流
7. 任务入队(Redis Queue)
8. R Worker 从队列取任务:
a. 读取数据文件
b. 执行 ggplot2 代码(参数化)
c. ggsave 输出 PNG + PDF
d. 上传到 OSS
e. 返回图片 URL
9. 前端轮询/WebSocket 获取结果
10. 用户预览 + 下载

并发处理

  • 每个 R Worker 是一个 Docker 容器,单线程处理一个任务
  • 通过 Redis Queue 实现多 Worker 并行消费
  • 初期 4 个 Worker(4 核服务器),后期按负载自动扩缩容
  • 单个任务超时 30s,超时自动 kill

5. 安全与限制

维度策略
文件大小单文件 < 50MB
请求频率匿名用户 10 次/分钟,注册用户 60 次/分钟
数据隔离每个任务在独立临时目录,完成后清理
代码注入不执行用户代码,只接受参数化的预定义图类型
数据保留匿名用户数据 1 小时后删除;注册用户 7 天
HTTPS全站强制 HTTPS

6. 目录结构(建议)

biof3-plot/
├── frontend/ # Next.js 前端
│ ├── app/
│ │ ├── page.tsx # 首页(图类型选择)
│ │ ├── plot/[type]/ # 每种图的参数页
│ │ └── api/ # BFF API Routes
│ ├── components/
│ │ ├── FileUpload.tsx
│ │ ├── ParamPanel.tsx
│ │ └── PlotPreview.tsx
│ └── lib/
│ ├── plotTypes.ts # 图类型定义(复用 FigCode 的 _data.ts)
│ └── api.ts # 后端 API 调用

├── backend/
│ ├── r-worker/ # R 绘图服务
│ │ ├── Dockerfile
│ │ ├── plumber.R # API 入口
│ │ └── plots/ # 每种图的 R 函数
│ │ ├── volcano.R
│ │ ├── heatmap.R
│ │ └── ...
│ ├── python-worker/ # Python 绘图服务(备选)
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ └── plots/
│ └── queue/ # 任务队列配置
│ └── worker.ts

├── infra/
│ ├── docker-compose.yml # 本地开发
│ ├── docker-compose.prod.yml
│ ├── nginx.conf
│ └── deploy.sh

├── shared/
│ └── plot-types.json # 图类型定义(前后端共享)

└── README.md

7. 与 BioF3 的集成点

集成方式说明
FigCode "在线绘图" 按钮URL 指向 http://<IP>:<PORT>/plot/volcano
图类型定义共享_data.ts 里的 onlineToolUrl 字段指向对应页面
用户体系可选:共享 BioF3 的 Waline 用户系统,或独立注册
样式统一使用相同的配色(biof3_colors)和字体

8. 开发路线

阶段时间目标
Week 1-2搭建骨架Next.js 前端 + R Plumber 后端 + Docker Compose
Week 3-4MVP 3 种图Volcano + Heatmap + PCA,端到端跑通
Week 5-6扩展到 10 种图覆盖 FigCode 里最常用的 10 种
Week 7-8上线 + 优化部署到阿里云、性能优化、限流
后续持续迭代更多图类型、用户系统、pipeline 工具

9. 服务器资源估算(初期)

资源规格用途
计算服务器4C 8GAPI + R Worker × 4 + Redis + Nginx
前端同一台服务器或 VercelNext.js(初期和 API 同机部署)
存储阿里云 OSS 100GB 或本地磁盘用户文件 + 生成图片
端口8080(前端)+ 3001(API)Nginx 统一代理
SSL后期按需配置初期 HTTP 即可

如果用户量增长,计算服务器可以横向扩展(加 Worker 容器)。


10. 技术风险与应对

风险应对
R 包版本冲突Docker 镜像锁定版本,每种图类型可以用不同镜像
大文件处理慢前端预处理(采样/截断)+ 后端流式读取
并发高峰Redis 队列 + Worker 自动扩缩容
恶意请求限流 + 文件类型白名单 + 沙箱执行
R 进程崩溃每个任务独立进程,超时 kill,不影响其他任务

11. 下一步行动

  1. 在 OmicsHub 项目中新增绘图模块(前端路由 + 后端 Worker)
  2. 复用 OmicsHub 已有的用户系统、文件存储、API 框架
  3. 新增 R Worker 容器(Docker),实现第一个图类型(Volcano Plot)
  4. 本地验证端到端流程
  5. 把 BioF3 FigCode 的 onlineToolUrl# 改成 OmicsHub 的真实 URL

文档版本:v1.1 | 2026-05-12 | 作者:BioF3 团队 目标项目:OmicsHub(绘图模块)

静态文件

离线资料下载

手册 HTML / PDF 已在后台预生成,点击后直接下载网站静态资源。