onfailure=无法直接判断连续闪退,需通过守卫服务统计失败频次并触发报警;守卫服务用/run/下时间戳文件或systemd-run计时器实现5分钟内3次失败的判定,再调用oneshot报警服务。
![systemd中怎么在[unit]区块中精细化配置onfailure属性实现当主业务服务连续闪退时自动激活备用通知服务发送报警](https://img.php.cn/upload/article/001/242/473/178667922866987.png?x-oss-process=image/resize,p_40)
在 systemd 中,OnFailure= 本身不支持“连续闪退”这类状态判断,它只响应单次启动失败(即服务进入 failed 状态),无法直接感知失败频次或时间窗口。要实现“主服务连续闪退时触发备用报警”,必须结合 systemd 的原生机制与外部逻辑协同完成——核心思路是:用 OnFailure= 触发一个守卫单元(guard unit),由该单元负责统计失败次数、判定是否达到阈值,并按需激活报警服务。
1. 基础配置:用 OnFailure 绑定到守卫服务
在主业务服务(如 app.service)的 [Unit] 区块中,指定失败时调用一个专用的守卫服务:
[Unit] Description=Main Application Service OnFailure=notify-failure-guard@%i.service
使用 @%i 可传递实例名(如 app),便于守卫服务区分不同主服务;若为非模板服务,可写死为 notify-failure-guard.service。
2. 守卫服务设计:记录失败、判断连续性
守卫服务(如 notify-failure-guard@.service)需具备状态记忆能力。systemd 本身无内置计数器,推荐两种轻量方案:
-
使用 systemd 的临时文件 + 时间戳判断:在
/run/下维护一个带时间戳的失败记录文件(如/run/app-failures.log),每次被OnFailure触发时追加当前时间,再读取最近 5 分钟内的条目数。超过阈值(如 3 次)则启动报警服务。 -
用 systemd-run 动态生成一次性计时器:首次失败时启动一个 5 分钟的
.timer,期间所有失败都写入同一临时文件;计时器到期后自动清理。这样避免长期守护进程,更符合 systemd 哲学。
示例守卫服务片段(简化版,依赖外部脚本):
[Service] Type=oneshot ExecStart=/usr/local/bin/check-and-notify.sh %I RemainAfterExit=yes
其中 %I 是实例名(如 app),脚本内做时间窗口统计与阈值判断。
3. 报警服务:作为独立单元按需激活
报警服务(如 alert-on-call.service)应设为 Type=oneshot,且不设置 WantedBy= 或自动启动,仅通过守卫服务的 ExecStart 或 systemctl start 显式触发:
[Service] Type=oneshot ExecStart=/usr/bin/send-alert.sh "app crashed 3 times in 5 minutes"
确保该服务有适当权限访问邮件/短信/钉钉等通知通道,并做好错误兜底(如通知失败时不阻塞主流程)。
4. 关键细节与避坑提醒
以下几点直接影响可靠性:
-
OnFailure只在服务进入failed状态时触发,若服务崩溃后被Restart=on-failure自动拉起成功,则不会触发——因此守卫逻辑必须在“失败发生瞬间”捕获,不能依赖重启后的状态。 - 不要把计数逻辑放在
ExecStartPre或ExecStopPost中:这些钩子不保证执行顺序,且可能因服务未完全退出而读不到最新状态。 - 建议用
/run/而非/tmp/存失败日志:前者是内存文件系统,重启即清空,天然契合“连续失败”场景的时间边界。 - 若主服务启用
RestartSec=,守卫服务应在ExecStart开头加sleep 0.1等微小延迟,避免与主服务重启竞争文件锁。
不复杂但容易忽略。











