单机跑全量回归要 4 小时,发版窗口只有 90 分钟——分布式执行机不是为了"炫技",是被发版节奏逼出来的。这篇记录一套轻量分布式测试执行架构:不引入 Kubernetes,N 台 Windows 机器 + 一个调度中心,把 4 小时压进 90 分钟。
一、架构总览
1 | ┌─────────────┐ |
关键决策:执行机是无状态的 worker。用例代码、配置、依赖全部从调度中心拉取(git 仓库 + 配置包),执行机只负责"给我一个任务 ID,我跑完把结果吐回来"。加机器 = 装个 agent + 配个 token,零代码变更。
二、任务切分:用例 → 任务 → 槽位
切分粒度决定了并行效率和稳定性,三层结构:
1 | class Scheduler: |
为什么按平台聚组:同一平台(同一台设备/同一个 App 包)的用例放一个任务,避免一台机器上同时装 4 个不同平台——设备互斥、安装包冲突、代理端口打架,全来自跨平台混跑。
为什么单任务 15 分钟:经验值是"失败可定位"的上限——任务跑 15 分钟挂了,回看日志找现场是现实可行的;60 分钟的任务挂了基本只能重跑。
三、心跳与槽位:动态感知道具
执行机 agent 每 10s 上报:
1 | { |
槽位状态机:idle → busy_install → running → done/fault → idle。调度中心只往 idle 槽派任务;fault 槽自动隔离,连续 3 次 fault 的机器整机摘除并告警——一台坏机器不应该拖累整批回归。
四、结果回收与断点
每个用例结束立即上报,不等任务结束:
1 | # 执行机 agent(节选) |
断点续跑:任务失败(机器宕机、App 崩溃未恢复)时,调度中心看到"已上报 passed 的用例"和"计划用例"的差集,把差集重组成新任务重新派发——不需要重跑已经通过的用例。这是分布式回归里省钱的关键:一次全量回归中途挂 2 台机器,只重跑 20% 的用例。
五、规模数据(真实压测)
| 规模 | 全量回归(约 600 用例) | 对比单机 |
|---|---|---|
| 单机 8 槽 | 3h50m | 1.0× |
| 3 机 20 槽 | 1h18m | 3.0× |
| 5 机 32 槽 | 52m | 4.4× |
注意加速比不是线性的:任务切分粒度(15 分钟装箱)决定了尾部效应——最后一个任务要等它自己跑完。装箱越均匀,尾部越短,这也是贪心装箱里按预估时长排序的原因。
六、踩过的坑
- 时钟漂移:5 台机器的系统时间差 2s,导致"用例开始时间"排序全乱——agent 启动时强制 NTP 同步;
- 配置包版本:任务 A 用配置 v12 跑,任务 B 用 v13,结果对比时发现环境不一致——任务元数据里固化配置包 hash,报告头部展示;
- 孤儿进程:任务被 cancel 后 App 进程还活着,占着设备——cancel 语义必须包含"杀进程树 + 卸载代理",agent 本地兜底超时强杀。
七、小结
分布式执行机的设计原则就三条:worker 无状态(扩缩容零成本)、任务粒度 15 分钟(失败可定位)、结果逐例上报(断点可续跑)。不追求 K8s 那种通用编排,测试场景的"设备互斥 + 安装包互斥"用平台聚组解决,简单有效。