Gin进程在Supervisor下总显示STOPPED,根本原因是未以前台模式运行或存在panic未捕获、端口占用、配置错误、os.Exit()调用、信号处理不完整等问题,导致进程非0退出;需配置startsecs=0、autorestart=unexpected、redirect_stderr=true及stopasgroup=true等参数确保正确管理。

Supervisor 不能直接守护 Gin 程序,除非你明确让它以**前台模式运行**;Gin 默认启动后会立即进入事件循环并阻塞,但若二进制被误配为后台 daemon(比如加了 & 或由 shell 脚本 fork),Supervisor 就会认为它“启动即退出”,反复重启或显示 STARTING → STOPPED 循环。
为什么 Gin 进程在 Supervisor 下总显示 STOPPED?
根本原因是 Gin 编译后的二进制默认就是前台长期运行的,但 Supervisor 启动时会严格检查进程是否“真正存活”——它靠轮询子进程 PID 是否存在 + exit code 判断。如果 Gin 程序因以下任一原因提前退出,Supervisor 就判定失败:
-
panic未捕获,导致进程非 0 退出 - 监听端口被占用(如
listen tcp :8080: bind: address already in use),Gin 启动失败并退出 - 配置文件读取失败、环境变量缺失,
os.Exit(1)或 panic - 没加
defer server.Shutdown(context.Background()),信号处理不完整,收到SIGTERM后直接退出而非优雅关闭
此时 supervisorctl status mygin 会显示 EXITED,supervisorctl tail mygin stderr 才能看到真实错误。
必须设 startsecs=0 和 autorestart=unexpected
Gin 启动极快(毫秒级),不需要等待“稳定运行 X 秒才认定成功”。设 startsecs=10(默认值)会导致 Supervisor 等待 10 秒无响应后强制 kill 再重试,形成假死循环。
autorestart=unexpected 是关键:它只在进程以非 0 码退出、且该码不在 exitcodes 列表中时才重启。避免因正常调用 os.Exit(0)(如健康检查主动退出)被误判为崩溃。
示例最小可行配置片段:
[program:mygin] command=/opt/myapp/gin-server directory=/opt/myapp autostart=true autorestart=unexpected startsecs=0 exitcodes=0 redirect_stderr=true stdout_logfile=/var/log/mygin/access.log stderr_logfile=/var/log/mygin/error.log user=www-data stopasgroup=true killasgroup=true stopsignal=TERM
stopasgroup=true 和 killasgroup=true 不是可选项
Gin 程序本身不 fork,但如果你用了日志轮转库(如 lumberjack)、或集成 Prometheus 的 http.Server 并启用了 pprof,底层可能触发子线程/子进程。更常见的是:你在启动脚本里包装了 bash -c 'ulimit -n 65536 && ./gin-server' —— 此时 bash 是父进程,Gin 是孙子进程,Supervisor 默认只 kill bash,Gin 继续残留并占端口。
这两个参数强制 Supervisor 向整个进程组发信号,确保干净终止。缺一不可,否则 supervisorctl stop mygin 后 netstat -tuln | grep 8080 仍能看到端口被占。
日志看不到 panic?检查 redirect_stderr=true 和路径权限
Gin 的 log.Fatal、panic、HTTP handler 中未 recover 的异常,全部输出到 stderr。Supervisor 默认不捕获 stderr,必须显式开启:
- 务必设置
redirect_stderr=true(不是true字符串,是布尔值) -
stderr_logfile路径需对user(如www-data)可写,否则 Supervisor 静默失败,连启动日志都不写 - 不要依赖
stdout_logfile查崩溃——Gin 默认不把 panic 打印到 stdout
验证方法:sudo -u www-data /opt/myapp/gin-server 手动执行一次,看是否报错;再检查日志文件属主是否匹配。
最易忽略的一点:Gin 服务本身没有“守护进程”概念,它的稳定性完全取决于你是否堵住了所有 panic 出口、是否做了端口预检、是否用 http.Server.Shutdown 处理信号。Supervisor 只是兜底,不是免死金牌。


















