1. 持续集成与并行调度概述

1.1 持续集成到底在解决什么?

持续集成(Continuous Integration,简称CI)是我工作这么多年来,觉得最实用的研发实践之一。说白了就是:每次代码变更,都自动拉取、构建、跑测试,发现问题立刻反馈。你想想看,如果开发了一周才合并,冲突和Bug能堆成山。但真正让CI落地见效的,不是工具本身,而是怎么安排这些任务的执行顺序和方式。

我刚开始带团队的时候,用的就是最简单的串行CI——一个任务接一个任务的跑。那会项目小,倒也没觉得慢。可后来微服务一拆,模块一多,一次构建动辄半小时起步。等得让人抓狂。记得有一次线上紧急修复,push完代码后,CI跑了近50分钟。大家就盯着那个进度条干着急——这就是典型的串行瓶颈。

核心矛盾:开发团队规模增长 → 提交频率增加 → 串行排队时间暴涨 → 反馈延迟加剧 → 开发者开始绕过CI。

1.2 传统串行CI的痛点,你肯定遇到过

我把常见的几个坑总结了一下,你看看是不是感同身受:

  • 流水线排队严重:十个提交串行跑,最后一个要等前面九个全部完成。要是中间有个测试挂了,重新跑又得从零开始。
  • 资源利用率极低:构建机CPU经常在跑一个任务时闲置,其他模块只能干等。我算过一笔账,串行模式下资源利用率往往不到20%。
  • 修复成本高:串行跑到后半段才发现问题,中间依赖的模块已经白跑了。我曾经一个项目里,因为数据库迁移脚本顺序没处理好,导致后续十几个测试全部失败——全白跑。
  • 反馈链太长:开发者从push到收到结果,短则十几分钟,长则一小时以上。等结果的这段时间,人容易被打断,切换上下文成本很高。

避坑提醒:我曾经见过一个团队,为了追求“串行可控”,硬是把所有测试打包成一个Job。结果跑一次超过两小时。后来重构时才发现,因为历史原因,80%的测试根本不依赖其他模块——完全可以并行跑。所以,别为了让CI看起来“简单”而去牺牲效率。

1.3 并行调度:不只是“多开几个Job”那么简单

很多人一听到并行调度,第一反应就是“多开几台机器一起跑测试”。其实没这么简单。你想想看:如果两个任务都依赖同一个数据库资源,一起跑就会产生锁冲突。如果A任务需要B任务的产物,你乱并行就会拿不到数据。所以,并行调度的核心不是“同时跑”,而是“在依赖正确的前提下尽量同时跑”。

我最早试过简单的并行:比如把单元测试和代码检查放在不同容器里同步跑。效果立竿见影,时间降了一半。但很快又遇到新问题:集成测试要等部署完成,而部署又要等构建产物,这些依赖关系不梳理清楚,并行反而容易乱套。

这里有一个经验值:当项目模块数超过5个,并且交叉依赖超过3层时,手动维护并行策略基本不可行。这时候就需要依赖图来帮忙了。

串行 vs 简单并行 vs 依赖图并行 对比
维度串行简单并行依赖图并行
任务顺序线性固定固定分组并行按依赖动态排序
资源利用率低(<30%)中等(~60%)高(>85%)
依赖处理自动等待前序手动拆分,易冲突自动识别,保证正确
扩展性差,加模块需全改一般,分组需调整好,新增模块只需声明依赖
反馈速度慢中快

1.4 依赖图:打开并行调度的钥匙

依赖图(Dependency Graph)说白了就是把一个CI流水线里的所有任务,以及它们之间的前后关系,画成一张有向无环图(DAG)。从这张图里,你能一眼看出来:哪些任务可以同时跑,哪些必须按顺序。

举个例子:模块A和模块B之间没有依赖,那它们就可以并行;模块C依赖A和B,那C必须等A和B都完成才能启动。依赖图自动帮我们算出“每层可并发任务列表”,然后调度器按这个列表去分配资源。

我的一个习惯:每次新建一个CI流水线时,我先不急着写Jenkinsfile或GitHub Actions YAML,而是先画一张依赖草图。用方框表示任务,箭头表示依赖。画完之后,自然就知道哪些地方可以并行,哪些需要排队。这张草图,其实就是一个简化的依赖图。

下面这张图展示了从串行到依赖图并行调度的演进对比,也是本章的核心框架:

CI流水线调度演进对比 串行模式 代码提交 构建(单任务) 测试(排队) 部署(最末) 依赖图并行模式 代码提交 依赖图分析 模块A 模块B 模块C 合并验证 产出交付 串行:总时间 = 各任务时间之和 | 依赖图并行:总时间 = 关键路径上任务时间之和 理论上,并行度越高,CI反馈越快。但前提是依赖关系必须准确,否则就会出现数据竞争或无效跑测。

从图中你能直观看到:串行模式下所有任务排成一队,而依赖图模式把无依赖的任务拆成并行分支,只在必要汇合点等待。这种调度方式,就是我后面将要详细展开的核心方法。

小结

这一章我们聊了持续集成的本质、串行CI的明显短板,以及为什么不能简单粗暴地“并行跑”。关键点在于:依赖关系决定了哪些任务可以同时进行。依赖图就是描述这些关系的工具。后续所有章节都将围绕“如何构建依赖图、如何依据图来做任务调度、如何应对动态变化”展开。记住一句话:没有依赖图的并行,就是一团乱麻。

一个小练习:把你当前项目的CI流水线画出来,看看所有任务之间的依赖关系。数一数有几个任务其实可以并行却被迫串行。你会发现,90%的团队都有至少30%的优化空间。