我遇到了以下问题:我们有一个由 bash 脚本启动的 Java 应用程序。此应用程序应作为守护进程运行,因此我们有一个 upstart-job 来启动它。
start on runlevel [2345]
stop on runlevel [!2345]
#tell upstart we will fork later, so it will mangage the pids.
expect fork
#If the daemon stoppes unexpectedly, restart it!
respawn
script
#The framework will only work, if we start it from this directory.
cd /usr/lib/app-dir
nohup ./appStartScript.sh &> /dev/null &
#send an upstart event, in case we will chain this job later
emit app_running
end script
有时,应用程序会停止工作。没有 .hprof 文件,也没有通常在 VM 崩溃时创建的 hserr 文件。Upstart 报告应用程序正在运行,
appDeamon start/running, process 1131
但 PID 没有列在ps-aux。(此外,upstart 无法使用停止应用程序守护进程。
我想知道:a)为什么upstart没有意识到应用程序已经崩溃?b)是否有可能强制upstart重新启动应用程序,即使具有给定pid的进程不再存在?(到目前为止,我们需要重新启动整个服务器。)
我们的系统是 Ubuntu Linux 10.04.1 LTS。
答案1
守护进程通常会发生以下情况:
- Upstart 在前台运行可执行文件
- 该程序加载其配置文件,检查它,执行各种设置操作(如打开监听端口)。
- 如果上一步失败,程序将退出,并且 upstart 将获得一个非零的退出代码,从而知道它失败了
- 如果步骤 2 没有失败,程序现在会分叉,本质上会创建它的两个副本
- Upstart 最初执行的进程现在以零退出代码退出,表明它已成功
- 分叉的进程继续运行并完成应用程序的实际工作
问题是 Java 没有提供分叉机制,因此这种久经考验的模式无法正确实现。执行 Java 守护进程时,您被迫立即将进程置于后台(即&
脚本中的符号)。Upstart 本质上是启动进程,然后立即忘记它——进程无法向 Upstart 指示它是否成功启动。
解决这个问题的唯一方法是启动该进程,将其置于后台,然后检查它是否仍在运行,以确定它是否成功。当然,关键是确定什么时候检查它是否仍在运行。简单的解决方案如下:
#!/bin/sh
java MyClass >/dev/null 2>&1 &
PID=$!
sleep 3
if kill -0 $PID; then
exit 0
else
exit 1
fi
还有更多精心策划确定何时检查进程,例如让程序在完成启动例程时关闭 stdout 和 stderr 或创建其 PID 文件,并在启动脚本中等待这些事件。
对于您来说,最简单的解决方案是将 Upstart 脚本修改为如下所示的内容:
script
cd /usr/lib/app-dir
nohup ./appStartScript.sh &> /dev/null &
PID=$!
sleep 3
if kill -0 $PID; then
emit app_running
exit 0
else
exit 1
fi
end script
答案2
为什么您的应用程序需要通过脚本启动bash
?Upstart 需要知道您的应用程序分叉了多少次。您已告诉它它不会分叉(因为您未指定“ expect
”节),但您还是分叉了(因为您在脚本部分指定了“ &
”。因此,Upstart 无法跟踪 PID。
请参见: