Symfony Process组件stop方法不生效,调用后仍触发60秒超时错误
问题根因
- 执行顺序逻辑错误:
Process::run()是同步阻塞方法,调用后会一直阻塞等待进程执行完成、或触发默认的60秒超时抛出异常,才会继续执行后续代码。你当前将stop(15)写在run()之后,在进程运行阶段stop()根本不会被触发,等执行到stop()时要么进程已经结束、要么已经抛出了60秒超时错误。 - 默认超时优先级更高:Symfony Process默认启用了60秒的全局执行超时,即使你逻辑正确,如果你没有手动修改超时配置,进程运行到60秒时会直接抛出超时异常,不会等到你调用
stop()。 - 信号未传递到子进程:你使用
fromShellCommandline启动进程时,实际会先启动一个bash父进程,再由bash派生receive.sh子进程。默认情况下stop()发送的终止信号只会发给bash父进程,如果父进程没有把信号传递给子进程,receive.sh会继续运行,导致Process组件认为进程还未终止。
修复方案
1. 调整执行逻辑,改用异步启动
如果你需要主动控制进程的终止时机,不要用同步的run(),改用异步start()方法,自定义等待时长后调用stop(),同时关闭默认的60秒超时:
$process = Process::fromShellCommandline('bash ./receive.sh \ url=' . env('SGW_URL') . ' \ cert=certs/privateKey.p12?' . env('SGW_CERT_PASS'), '/var/www/sgwClientFiles/'); // 关闭默认60秒超时,也可设置为你预期的最大超时时间,比如75秒 $process->setTimeout(null); // 异步启动进程,不会阻塞 $process->start(); // 等待15秒 sleep(15); // 终止进程,第二个参数可指定先发送SIGTERM,超时未终止再发SIGKILL $process->stop(10, SIGTERM); if (!$process->isSuccessful()) { throw new ProcessFailedException($process); } return $process;
2. 解决子进程信号传递问题
如果调整后还是出现进程无法终止的情况,可以修改启动命令,用exec替换bash进程,让receive.sh直接成为主进程,信号可以直接发到脚本进程:
// 把bash换成exec bash,让receive.sh接管进程ID $process = Process::fromShellCommandline('exec bash ./receive.sh \ url=' . env('SGW_URL') . ' \ cert=certs/privateKey.p12?' . env('SGW_CERT_PASS'), '/var/www/sgwClientFiles/');
如果脚本本身忽略SIGTERM信号,可以在调用stop()时指定直接发送SIGKILL强制终止,或者给脚本添加信号处理逻辑。
内容的提问来源于stack exchange,提问作者Mr_T
相关产品推荐
相关产品推荐

