今天跟大家聊聊我这几天在搞的“undead”项目,说白了就是想搞个“不死”应用,听起来是不是挺唬人的?就是想让我的程序在挂掉之后能自动重启,保证服务的稳定性。
我琢磨着最简单的办法,直接写个死循环的脚本,在里面跑我的主程序,挂了就再跑一遍。结果发现这玩意儿太粗暴了,CPU直接干到100%,而且没法区分是正常退出还是崩溃,直接无脑重启。不行,pass!
然后我就开始研究 `systemd`,这玩意儿是 Linux 下的 init 系统,可以用来管理服务。我记得之前配过 Nginx 的自动启动,感觉挺靠谱的。于是开始折腾 `systemd` 的 service 文件。
我得建一个 `.service` 文件,位置一般在 `/etc/systemd/system/` 目录下。文件内容大概长这样:
[Unit]
Description=My Awesome Undead App
After=*
[Service]
User=myuser
WorkingDirectory=/path/to/my/app
ExecStart=/path/to/my/app/*
Restart=on-failure
RestartSec=5
[Install]
WantedBy=*
这里面 `ExecStart` 就是启动命令,`Restart=on-failure` 表示程序挂了就重启,`RestartSec=5` 表示重启间隔 5 秒。
写完之后,赶紧 `systemctl daemon-reload` 重新加载一下配置,然后 `systemctl enable *` 设置开机自启, `systemctl start *` 启动服务。
试了一下,崩溃之后果然能自动重启了!感觉还不错。
但是问题又来了,如果我的程序是正常退出?比如收到一个信号要优雅地关闭,这时候我也要重启,这就不对了。
于是我又开始研究 `systemd` 的配置,发现有个 `Type=forking` 选项,可以用来区分是前台运行还是后台运行。我的程序是前台运行的,所以不用设置这个。
后来我发现,可以通过 `ExitCode` 来判断退出状态码。正常退出的话,状态码一般是 0,异常退出的话,状态码是非 0 值。
我修改了我的 `*` 脚本,在程序退出后,判断状态码,如果是 0,就手动 `exit 0`,如果不是 0,就啥也不干,让 `systemd` 去重启。
bash
#!/bin/bash
/path/to/my/app/my_app
exit_code=$?
if [ $exit_code -eq 0 ]; then
exit 0
fi
这样一来,只有程序异常退出,`systemd` 才会重启,正常退出就不会了。
搞完这些,基本就满足了我的需求。程序挂了自动重启,正常退出就拉倒。
这只是个最简单的实现,还有很多可以优化的地方,比如:
可以加个监控,如果程序一直重启,说明有问题,要发个告警。
可以把日志记录到文件里,方便排查问题。
可以设置资源限制,防止程序吃光 CPU 和内存。
这回折腾 `systemd` 还是学到了不少东西,以后再遇到类似的需求,就能更快地解决。希望这回的分享对大家有所帮助!