14 KiB
14 KiB
R 统计引擎架构与部署指南
版本: v1.0
创建日期: 2026-02-19
维护者: SSA-Pro 开发团队
状态: ✅ 生产就绪
📋 目录
1. 概述
1.1 什么是 R 统计引擎
R 统计引擎是平台的专用统计计算服务,基于 Docker 容器化部署,提供:
- 🧮 严谨的统计分析能力(T 检验、方差分析、回归等)
- 🛡️ 统计护栏(正态性检验、方差齐性检验等)
- 📊 可视化输出(Base64 编码的图表)
- 📝 可复现代码生成(APA 格式的 R 脚本)
1.2 定位
┌─────────────────────────────────────────────────────────────┐
│ 业务模块层 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ SSA-Pro │ │ 其他 │ │ 其他 │ │
│ │ 智能统计 │ │ 模块 │ │ 模块 │ │
│ └────┬────┘ └─────────┘ └─────────┘ │
├───────┼─────────────────────────────────────────────────────┤
│ ▼ 通用能力层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ R 统计引擎 (Docker) │ │
│ │ • /health 健康检查 │ │
│ │ • /api/v1/tools 工具列表 │ │
│ │ • /api/v1/skills 技能执行 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
1.3 技术栈
| 组件 | 版本 | 说明 |
|---|---|---|
| R | 4.3.3 | 统计计算核心 |
| plumber | 1.2.1 | REST API 框架 |
| ggplot2 | 最新 | 数据可视化 |
| car | 3.1-2 | 高级统计检验 |
| dplyr/tidyr | 最新 | 数据处理 |
| Docker | 24+ | 容器化部署 |
2. 架构设计
2.1 Brain-Hand 模型
R 统计引擎采用 Brain-Hand 分离架构:
┌──────────────────┐ ┌──────────────────┐
│ Node.js │ │ R Docker │
│ (Brain) │ │ (Hand) │
├──────────────────┤ ├──────────────────┤
│ • 业务逻辑 │ HTTP │ • 统计计算 │
│ • 认证鉴权 │ ───────> │ • 数据处理 │
│ • OSS 签名 │ │ • 图表生成 │
│ • 结果解释 │ <─────── │ • 代码生成 │
└──────────────────┘ JSON └──────────────────┘
2.2 数据传输协议
支持两种数据传输方式:
| 方式 | 条件 | 字段 |
|---|---|---|
| inline | 数据 < 2MB | data_source.data (JSON) |
| oss | 数据 >= 2MB | data_source.oss_url (预签名 URL) |
// 方式 1: inline
{
"data_source": {
"type": "inline",
"data": [{"group": "A", "value": 10}, ...]
}
}
// 方式 2: oss (预签名 URL)
{
"data_source": {
"type": "oss",
"oss_url": "https://bucket.oss.com/data.csv?signature=xxx"
}
}
2.3 安全设计
| 安全措施 | 实现方式 |
|---|---|
| 非特权用户 | USER appuser |
| 路径遍历防护 | tool_code 正则白名单 ^[A-Z][A-Z0-9_]*$ |
| OSS 密钥隔离 | Node.js 生成预签名 URL,R 无需持有密钥 |
| 健康检查 | Docker HEALTHCHECK |
3. Docker 镜像构建
3.1 完整 Dockerfile
FROM rocker/r-ver:4.3
LABEL maintainer="dev-team@aiclinicalresearch.com"
LABEL version="1.0.1"
LABEL description="SSA-Pro R Statistics Service"
# 安装系统依赖(包括 R 包编译所需的库)
RUN apt-get update && apt-get install -y \
libcurl4-openssl-dev \
libssl-dev \
libxml2-dev \
libsodium-dev \
zlib1g-dev \
libnlopt-dev \
liblapack-dev \
libblas-dev \
gfortran \
pkg-config \
cmake \
curl \
&& rm -rf /var/lib/apt/lists/*
# 直接安装 R 包
RUN R -e "install.packages(c( \
'plumber', \
'jsonlite', \
'ggplot2', \
'glue', \
'dplyr', \
'tidyr', \
'base64enc', \
'yaml', \
'car', \
'httr' \
), repos='https://cloud.r-project.org/', Ncpus=2)"
# 安全加固:创建非特权用户
RUN useradd -m -s /bin/bash appuser
WORKDIR /app
# 复制应用代码
COPY plumber.R plumber.R
COPY utils/ utils/
COPY tools/ tools/
COPY tests/ tests/
# 设置目录权限
RUN chown -R appuser:appuser /app
# 切换到非特权用户
USER appuser
EXPOSE 8080
# 环境变量
ENV DEV_MODE="false"
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# 启动服务
CMD ["R", "-e", "plumber::plumb('plumber.R')$run(host='0.0.0.0', port=8080)"]
3.2 系统依赖说明
| 依赖包 | 用途 |
|---|---|
libcurl4-openssl-dev |
httr 包(HTTP 请求) |
libssl-dev |
openssl 包(加密) |
libxml2-dev |
xml2 包(XML 解析) |
libsodium-dev |
sodium 包(加密) |
zlib1g-dev |
httpuv 包(Web 服务器) |
libnlopt-dev |
nloptr 包(优化算法) |
liblapack-dev |
线性代数计算 |
libblas-dev |
基础线性代数 |
gfortran |
Fortran 编译器(部分 R 包需要) |
cmake |
nloptr 包构建 |
curl |
健康检查 |
3.3 构建命令
# 本地构建
cd r-statistics-service
docker build -t ssa-r-statistics:1.0.1 .
# 查看镜像
docker images ssa-r-statistics
# 预期输出
REPOSITORY TAG IMAGE ID CREATED SIZE
ssa-r-statistics 1.0.1 xxxxxxxxxxxx x minutes ago 1.81GB
3.4 构建时间参考
| 阶段 | 耗时 |
|---|---|
| 基础镜像下载 | ~2 分钟(首次) |
| 系统依赖安装 | ~1 分钟 |
| R 包安装 | ~6 分钟 |
| 总计 | ~9 分钟 |
4. 部署指南
4.1 开发环境
使用 docker-compose:
# r-statistics-service/docker-compose.yml
version: '3.8'
services:
ssa-r-service:
build: .
container_name: ssa-r-statistics
ports:
- "8082:8080" # 主机8082 → 容器8080(REDCap占用8080/8081)
environment:
- DEV_MODE=true
volumes:
# 开发环境挂载:支持热重载
- ./tools:/app/tools
- ./utils:/app/utils
- ./tests:/app/tests
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"] # 容器内部仍是8080
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
启动命令:
cd r-statistics-service
docker-compose up -d
4.2 生产环境 (SAE)
# SAE 配置
容器镜像: registry.cn-beijing.aliyuncs.com/aiclinical/ssa-r-statistics:1.0.1
实例规格: 2 vCPU, 4 GB
最小实例: 1
最大实例: 5
端口: 8080
环境变量:
DEV_MODE: "false"
4.3 环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
DEV_MODE |
false |
开发模式(启用热重载,每次请求重新加载工具脚本) |
说明:开发环境和生产环境都使用真实 OSS,无需 Mock 数据。
- 开发环境:
ai-clinical-data-devbucket- 生产环境:
ai-clinical-databucket
4.4 端口配置
| 环境 | 主机端口 | 容器端口 | 说明 |
|---|---|---|---|
| 开发环境 | 8082 | 8080 | 避免与 REDCap 8080/8081 冲突 |
| 生产环境 (SAE) | 8080 | 8080 | 云端无端口冲突 |
注意:Node.js 后端通过
R_SERVICE_URL环境变量配置 R 服务地址,默认值为http://localhost:8082。
5. API 参考
5.1 健康检查
GET /health
响应:
{
"status": "ok",
"timestamp": "2026-02-19 08:00:00",
"version": "1.0.1",
"dev_mode": true,
"tools_loaded": 1
}
5.2 工具列表
GET /api/v1/tools
响应:
{
"status": "ok",
"tools": ["t_test_ind", "anova_oneway"],
"count": 2
}
5.3 执行技能
POST /api/v1/skills/{tool_code}
Content-Type: application/json
请求体:
{
"data_source": {
"type": "inline",
"data": [...]
},
"params": {
"group_var": "group",
"value_var": "value"
},
"guardrails": {
"check_normality": true
}
}
成功响应:
{
"status": "success",
"message": "分析完成",
"warnings": null,
"results": {
"method": "Welch Two Sample t-test",
"statistic": -5.196,
"df": 5.98,
"p_value": 0.002,
"p_value_fmt": "p = .002"
},
"plots": ["data:image/png;base64,..."],
"trace_log": [...],
"reproducible_code": "..."
}
错误响应:
{
"status": "error",
"error_code": "E001",
"error_type": "business",
"message": "列名 'xxx' 在数据中不存在",
"user_hint": "请检查变量名是否拼写正确"
}
6. 开发指南
6.1 添加新工具
- 在
tools/目录创建 R 脚本:
# tools/my_analysis.R
#' @tool_code ST_MY_ANALYSIS
#' @name 我的分析工具
#' @version 1.0.0
# 统一入口函数
run_analysis <- function(input) {
# 加载数据
df <- load_input_data(input)
# 参数
p <- input$params
# 护栏检查
# ...
# 核心计算
# ...
# 返回结果
return(list(
status = "success",
message = "分析完成",
results = list(...)
))
}
-
重启服务(开发模式无需重启)
-
测试:
curl -X POST http://localhost:8082/api/v1/skills/ST_MY_ANALYSIS \
-H "Content-Type: application/json" \
-d '{"data_source": {...}, "params": {...}}'
6.2 工具命名规范
| 项目 | 规范 |
|---|---|
| 文件名 | 小写下划线:t_test_ind.R |
| tool_code | 大写下划线:ST_T_TEST_IND |
| 入口函数 | 固定名称:run_analysis |
6.3 结果格式规范
return(list(
status = "success" | "error" | "blocked",
message = "...",
warnings = c("...") | NULL,
results = list(
# 统计结果
),
plots = list(
"data:image/png;base64,..."
),
trace_log = c("..."),
reproducible_code = "..."
))
7. 运维指南
7.1 日志查看
# 实时日志
docker logs -f ssa-r-statistics
# 最近 100 行
docker logs --tail 100 ssa-r-statistics
7.2 性能监控
# 容器资源使用
docker stats ssa-r-statistics
7.3 重启服务
# 开发环境
docker-compose restart
# 生产环境 (SAE)
通过 SAE 控制台重启实例
7.4 镜像更新
# 1. 构建新镜像
docker build -t ssa-r-statistics:1.0.2 .
# 2. 推送到镜像仓库
docker tag ssa-r-statistics:1.0.2 registry.cn-beijing.aliyuncs.com/aiclinical/ssa-r-statistics:1.0.2
docker push registry.cn-beijing.aliyuncs.com/aiclinical/ssa-r-statistics:1.0.2
# 3. 更新 SAE 部署
8. 常见问题
Q1: 构建时 httpuv 安装失败
错误: fatal error: zlib.h: No such file or directory
解决: 添加 zlib1g-dev 到系统依赖
Q2: 构建时 nloptr 安装失败
错误: CMAKE NOT FOUND
解决: 添加 cmake 到系统依赖
Q3: /tmp 权限问题
错误: cannot open file '/tmp/Rtmpxxx': No such file or directory
解决: 不要在启动命令中清理 /tmp
Q4: DEV_MODE 热重载不生效
原因: 没有挂载 volumes
解决:
volumes:
- ./tools:/app/tools
Q5: 容器启动后无法访问
检查:
- 端口映射是否正确
- 健康检查是否通过
- 查看容器日志
附录:文件结构
r-statistics-service/
├── Dockerfile # 生产镜像定义
├── docker-compose.yml # 开发环境编排
├── renv.lock # R 包版本锁定(备用)
├── .Rprofile # R 启动配置(备用)
├── plumber.R # API 入口
├── utils/
│ ├── data_loader.R # 数据加载(预签名 URL)
│ ├── guardrails.R # 统计护栏
│ ├── error_codes.R # 错误映射
│ └── result_formatter.R # 结果格式化
├── tools/
│ └── t_test_ind.R # 独立样本 T 检验
├── tests/
│ └── fixtures/
│ └── normal_data.csv # 测试数据
├── metadata/ # 工具元数据(预留)
└── templates/ # 解释模板(预留)
文档结束