需修复Dredd无法终止main.go进程问题,求CircleCI兼容方案
嘿,我碰到过类似的Dredd进程残留问题,尤其是在CI环境里踩过坑,给你几个靠谱的解决方案,特别是适配CircleCI的!
首先得说你之前的脚本为啥没用:go run main.go其实会先编译出临时二进制文件,然后启动这个二进制进程——也就是说$!拿到的是go run这个父进程的PID,而不是真正运行你的服务的子进程PID,所以kill那个父进程后,子进程可能还在跑,导致端口被占。
下面是几种可行的方案:
1. 改进Shell脚本,精准捕获服务进程PID
既然go run会衍生子进程,我们可以直接匹配服务进程的名称来获取PID,而不是依赖$!。修改你的test.sh如下:
#!/bin/sh # 先清理可能残留的同名进程(避免CI环境里的旧进程) pkill -f main.go || true # 启动服务到后台 go run main.go & # 等待服务完全启动(时间根据你的服务启动速度调整) sleep 3 # 获取真正的服务进程PID SERVICE_PID=$(pgrep -f "main\.go") # 运行Dredd测试 dredd apiary.apib http://localhost:5000/ RESULT=$? # 强制终止服务进程 if [ -n "$SERVICE_PID" ]; then kill -9 "$SERVICE_PID" fi # 再次清理可能的残留进程(保险起见) pkill -f main.go || true exit $RESULT
这里用pgrep -f "main\.go"精准匹配你的服务进程,pkill用来提前清理和事后兜底,避免CI环境里的遗留进程干扰。
2. 用Dredd Hooks(最优雅的官方方案)
Dredd本身支持生命周期钩子,可以在测试开始前启动服务,测试结束后自动终止服务,完全不用手动管理PID,这也是官方推荐的方式。
比如写一个Node.js的钩子文件(Dredd默认支持JS钩子,你需要在CI环境里安装Node.js):
const { spawn } = require('child_process'); let serverProcess; // 测试开始前启动服务 beforeAll((done) => { // 用spawn而不是exec,方便管理进程组 serverProcess = spawn('go', ['run', 'main.go'], { stdio: 'inherit' // 可选:把服务的输出同步到终端,方便调试 }); // 等待服务启动完成(时间按需调整) setTimeout(done, 3000); }); // 测试结束后终止服务 afterAll((done) => { if (serverProcess) { // 杀掉整个进程组,确保所有子进程都被终止 process.kill(-serverProcess.pid, 'SIGKILL'); } done(); });
然后运行Dredd的时候指定这个钩子文件:
dredd apiary.apib http://localhost:5000/ --hooks ./dredd-hooks.js
这种方式的好处是Dredd完全掌控服务的生命周期,不会出现进程残留,而且调试起来更清晰。
3. CircleCI专属配置优化
在CircleCI的config.yml里,你可以把服务启动、测试、清理的步骤整合起来,同时利用CircleCI的容器特性,确保每次测试都是干净的环境:
version: 2.1 jobs: dredd-test: docker: - image: circleci/golang:1.20 # 用你项目对应的Go版本镜像 steps: - checkout - run: name: 安装Dredd command: sudo npm install -g dredd # 因为Dredd是Node.js工具 - run: name: 运行Dredd测试 command: | # 提前清理可能的残留进程 pkill -f main.go || true # 启动服务到后台 go run main.go & # 等待服务启动 sleep 3 # 执行测试 dredd apiary.apib http://localhost:5000/ # 测试后强制清理进程 pkill -f main.go || true
如果用Hooks方案,把安装Node.js的步骤加上,然后运行带hooks的Dredd命令即可。
总结
优先推荐Dredd Hooks方案,因为它是官方设计的服务生命周期管理方式,最可靠也最优雅;如果不想用Node.js,改进后的Shell脚本也能解决问题,配合CircleCI的清理步骤,基本不会再出现端口占用的问题。
内容的提问来源于stack exchange,提问作者Kaigo

