服务挂了却没重启?先别怪 Restart 配置

部署在 Linux 上的 Spring Boot 服务,有时进程退出后并没有被 systemd 自动拉起。很多人第一反应是 Restart 配置写错了,但问题往往藏在 systemd 的三层判断机制里。

systemd 管理服务时,会依次检查退出状态、重启限频和 Type 设置。任何一个环节不满足,服务都不会被重启。

第一层:退出状态码

systemd 根据服务进程的退出码判断是否应该重启。如果退出码是 0(正常退出),systemd 默认不会重启;只有非零退出码才触发重启逻辑。Spring Boot 应用如果因为正常关闭(如执行 shutdown)而退出,退出码为 0,systemd 会认为这是预期行为,不会拉起。

第二层:重启限频

即使退出码非零,systemd 还有重启限频机制。默认情况下,如果服务在 10 秒内重启超过 5 次,systemd 会放弃重启,并进入 failed 状态。这是为了防止服务陷入崩溃循环。如果 Spring Boot 应用启动后立即崩溃,反复触发重启,最终会被 systemd 判定为失败,不再拉起。

第三层:Type=exec 的影响

Type 设置决定了 systemd 如何判断服务启动成功。Type=exec 表示只有当主进程 exec 成功后才认为服务启动完成。如果 Spring Boot 应用在启动过程中有子进程或脚本,Type=exec 会等待主进程完全就绪。如果配置不当,可能导致 systemd 误判服务状态,影响重启行为。

排查步骤:从日志到配置

遇到服务未重启,可以按以下顺序排查:

  1. 查看服务状态:systemctl status <service>,确认服务是否处于 failed 状态。
  2. 检查退出码:journalctl -u <service> 查看日志,找到进程退出时的状态码。
  3. 核对 Restart 配置:确认 Restart=on-failure 或 Restart=always 是否设置正确。
  4. 检查限频:如果服务频繁重启,查看是否触发了 StartLimitIntervalSec 和 StartLimitBurst 限制。
  5. 验证 Type 设置:如果使用 Type=exec,确保主进程路径正确,且没有其他干扰。

延伸:参数校验的最佳实践

服务稳定运行后,接口参数校验是保证质量的关键。Spring Boot 提供了从手动 if 校验到注解驱动校验的进阶路径。

手动 if 校验虽然直观,但代码冗余,且容易遗漏。注解驱动校验(如 @Valid、@NotNull、@Size 等)能减少样板代码,让校验逻辑集中在实体类上。配合全局异常处理器,可以统一返回校验错误信息,提升开发效率。

建议在项目初期就引入注解校验,并定义清晰的错误响应格式。这样既能保证接口健壮性,也能减少后续维护成本。

总结

systemd 的三层判断机制是服务重启的隐形门槛,理解退出状态、限频和 Type 设置,能快速定位问题。同时,参数校验作为接口开发的基础,采用注解驱动能显著提升代码质量。两者结合,能让 Spring Boot 服务更稳定、更可靠。

参考来源