Java进程返回退出码1时如何终止supervisord主进程?
问题排查与解决方案
问题背景
我通过supervisord管理3个子进程,其中Java进程sgw的配置如下:
[program:sgw] command=java -jar test.jar process_name=%(program_name)s autostart=true autorestart=unexpected exitcodes=1 killasgroup=true stopasgroup=true stopsignal=TERM startsec=0
配置autorestart=unexpected和exitcodes=1是为了让Java进程返回退出码1时不再重启。同时我希望该Java进程退出时终止supervisord并停止另外两个子进程,于是配置了事件监听器:
[eventlistener:processes] command=/bin/bash -c ". /etc/stop-supervisor.sh" events=PROCESS_STATE_STOPPED, PROCESS_STATE_EXITED, PROCESS_STATE_FATAL, PROCESS_STATE_UNKNOWN, PROCESS_STATE_BACKOFF
对应的脚本/etc/stop-supervisord.sh内容:
#!/bin/bash printf "READY\n"; while read line; do echo "Processing Event: $line" >&2; kill -3 $(cat "/data/logs/supervisord.pid") done < /dev/stdin
目前Java进程已退出,但supervisord仍在运行,另外两个子进程也未停止。
问题根源
- 信号选择错误:
kill -3发送的是QUIT信号,supervisord收到该信号后只会尝试优雅关闭,但默认不会主动终止所有子进程,且部分场景下可能无法正常退出。 - 脚本执行方式不当:使用
. /etc/stop-supervisor.sh是source脚本而非直接执行,可能导致执行上下文异常,无法正确触发kill操作。 - 事件监听范围冗余且未做进程过滤:监听多种状态事件但未判断触发事件的进程是否为目标Java进程,同时可能因为事件触发逻辑不匹配导致脚本未执行。
修复步骤
1. 调整事件监听器配置
修改监听器的执行命令为直接调用脚本,并缩小事件监听范围到PROCESS_STATE_EXITED(Java进程退出码1会触发该事件):
[eventlistener:processes] command=/etc/stop-supervisor.sh events=PROCESS_STATE_EXITED user=root # 根据supervisord运行用户调整,确保拥有kill权限
2. 修正停止脚本逻辑
更新/etc/stop-supervisord.sh,添加进程过滤逻辑,并使用TERM信号终止supervisord(supervisord收到TERM信号会自动停止所有子进程):
#!/bin/bash printf "READY\n"; while read -r event_line; do echo "Processing event: $event_line" >&2 # 从事件行中解析出进程名 target_process=$(echo "$event_line" | awk -F':' '{print $2}' | awk '{print $1}') # 仅处理目标Java进程sgw的退出事件 if [ "$target_process" = "sgw" ]; then # 发送TERM信号终止supervisord,触发所有子进程停止 kill -TERM $(cat "/data/logs/supervisord.pid") # 退出循环避免重复执行 exit 0 fi done < /dev/stdin
给脚本添加可执行权限:
chmod +x /etc/stop-supervisord.sh
3. 优化supervisord全局配置(可选)
在supervisord主配置文件中添加超时停止配置,确保子进程在超时后被强制终止:
[supervisord] stopwaitsecs=10 # 等待子进程优雅退出的超时时间,超时后发送KILL信号
4. 验证配置
重启supervisord加载新配置:
supervisorctl reload
测试Java进程退出后,检查supervisord是否终止,同时另外两个子进程是否已停止。可以通过查看supervisord日志确认事件触发情况:
tail -f /data/logs/supervisord.log # 替换为实际日志路径
内容的提问来源于stack exchange,提问作者Ganesh Shinde
相关产品推荐
相关产品推荐

