Nextflow 的三板斧:进程、通道、工作流与配置文件
说实话,刚接触 Nextflow 的时候,我也被那一堆概念搞得有点懵。什么进程啊、通道啊,听着跟操作系统似的。但用久了你会发现,其实核心就四个东西:进程(process)、通道(channel)、工作流(workflow),外加一个配置文件(配置文件)。
我个人习惯把这四个东西比作一条流水线:
- 进程就是流水线上的工人,每个工人只干一件事。
- 通道是传送带,把半成品从一个工位送到下一个。
- 工作流是车间主任,决定谁先干、谁后干、干完了交给谁。
- 配置文件是车间管理手册,记着所有工位的参数和设置。
嗯,这么一想是不是就清晰多了?下面我们一个一个说。
进程(process)—— 干活的基本单元
进程是 Nextflow 里最小的执行单位。你想想看,每个进程就做一件事:接收输入、执行命令、产生输出。我在做空间转录组质控时,最常用的进程就是跑 FastQC、跑 STARsolo、跑 EmptyDrops 这些。每个进程都是一个独立的“黑盒子”。
来,看一个最简单的进程定义:
process sayHello {
input:
path x
script:
"""
echo "Hello, $x!"
"""
}
这里有几个关键点:
- input 声明这个进程要吃什么。上面这个进程吃一个文件路径。
- script 里写实际要运行的命令。支持 Shell、Python、R 等。
- 进程名是驼峰式命名,我习惯用动词开头,比如
runFastQC、mergeReports。
script 块尽量保持简单。我在项目中遇到过把几百行 Python 直接塞进进程的情况,调试起来简直灾难。最好只写核心命令,复杂逻辑单独写成脚本文件,用 path 传进去。
通道(channel)—— 数据的“传送带”
通道是 Nextflow 里最让我觉得巧妙的设计。它本质上是一个异步的、先进先出的数据队列。进程从通道里读数据,处理完再往通道里写数据。
两种最常用的通道创建方式:
// 从列表创建通道
ch_fastq = Channel.fromPath("/data/samples/*.fastq.gz")
// 从值创建通道
ch_params = Channel.value(params.outdir)
为什么会这样设计?因为 Nextflow 里的进程是并行执行的。你用通道把数据喂进去,Nextflow 会自动决定怎么分片、怎么调度。我在处理几十个空间切片时,就是一个通道把所有 FASTQ 路径传进去,Nextflow 自动并行跑,省心得很。
| 通道类型 | 创建方法 | 典型用途 |
|---|---|---|
| 值通道 | Channel.value() |
传递一个固定的配置或参数 |
| 队列通道 | Channel.fromPath() |
传递文件列表,自动并行分发 |
| 绑定通道 | channel.bind() |
手动向通道发送数据 |
.buffer() 或者 .into() 来复制通道。
工作流(workflow)—— 把一切串起来
工作流就是你告诉 Nextflow “先跑 A,再跑 B,最后跑 C”。在 Nextflow 里,进程调用的顺序由数据依赖决定,不是靠你写顺序。
举个例子:
workflow {
// 读取输入
input_ch = Channel.fromPath(params.fastq)
// 第一步:质控
fastqc_ch = runFastQC(input_ch)
// 第二步:比对
bam_ch = runSTARsolo(fastqc_ch)
// 第三步:计数
count_ch = runCellRanger(bam_ch)
}
你看,我并没有写“先执行 runFastQC,再执行 runSTARsolo”。Nextflow 自己会分析:runSTARsolo 等着 fastqc_ch 的输出,所以它必须等 runFastQC 跑完才会启动。
我个人很喜欢这种隐式的依赖管理。它让代码看起来更干净,也更容易调整流程顺序。你想想看,要是我突然想在比对之前加一步修剪,只需要在中间插一个进程,改改通道名就行。
配置文件(nextflow.config)—— 别把参数写死在代码里
我曾经犯过一个错:把所有参数都写在工作流脚本里。结果每次换数据集,或者要在不同集群上跑,就得改代码。后来我学乖了,用配置文件把参数抽出来。
一个典型的配置文件长这样:
// nextflow.config
params {
outdir = "./results"
fastq_dir = "/data/samples"
genome = "/ref/hg38.fa"
threads = 4
}
process {
executor = 'slurm'
queue = 'normal'
memory = '8 GB'
time = '2h'
}
在工作流里用 params.outdir、params.fastq_dir 来引用这些参数。你想换参数?改配置文件就行,代码不用动。
配置文件的加载顺序也很重要:
- 项目根目录下的
nextflow.config - 用户家目录下的
~/.nextflow/config - 命令行用
-c指定的配置文件
后面加载的会覆盖前面的。我一般在项目里放一个默认配置,在命令行里用 -c my_slurm.config 覆盖执行器设置。
到这里,四个核心概念就讲完了。让我用一张图总结一下它们的关系:
这张图里,数据从左往右流。输入数据被包装成通道,通道把数据分发给各个进程。工作流在背后管理依赖关系,确保进程按正确顺序执行。配置文件则为整个过程提供全局参数和执行环境配置。
这四个概念搞清楚了,Nextflow 的基本框架就通了。后面的章节我们会在这个基础上,一步步搭建空间质控的实际流程。