java:systemd-notify中的不一致看门狗超时

我的java应用程序安装在OpenSUSE 13.2操作系统上,我正在使用systemd进行进程控制。 (systemd版本210)

我想使用systemd-notify来利用systemd看门狗function。 但是,我注意到应用程序重新启动是由于看门狗的超时时间不一致。

使用WatchdogSec = 120,并且应用程序配置为每隔60秒调用systemd-notify,我观察平均每5到20分钟重新启动一次。

这是进程的(略微编辑的)systemd单元文件:

# Cool systemd service [Unit] Description=Something Awesome After=awesomeparent.service Requires=awesomeparent.service [Service] Type=simple WorkingDirectory=/opt/awesome Environment="AWESOME_HOME=/opt/awesome" User=awesomeuser Restart=always WatchdogSec=120 NotifyAccess=all ExecStart=/home/awesome/jre1.8.0_05/bin/java -jar awesome.jar [Install] WantedBy=multi-user.target 

这是调用systemd-notify的代码

 String pidStr = ManagementFactory.getRuntimeMXBean().getName(); pidStr = pidStr.split("@")[0]; String cmd = "/usr/bin/systemd-notify"; Process process = new ProcessBuilder(cmd, "MAINPID=" + pidStr, "WATCHDOG=1").redirectErrorStream(true) .start(); int exitCode = 0; if ((exitCode = process.waitFor()) != 0) { String output = IOUtils.toString(process.getInputStream()); Log.MAIN_LOG.error("Failed to notify systemd: " + ((output.isEmpty()) ? "" : " " + output) + " Exit code: " + exitCode); } 

在日志中,我从未看到失败消息(进程总是返回0退出代码)并且我100%确定任务正在每分钟执行一次,分钟。 我可以看到任务日志在重新启动之前立即执行。

任何人都有任何想法为什么systemd-notify有时不起作用?

我正在考虑编写代码来直接调用sd_pid_notify,但是想知道在走这条路线之前我是否可以做一个简单的配置。

这是解决问题的JNA代码:

 import com.sun.jna.Library; import com.sun.jna.Native; /** * The task issues a notification to the systemd watchdog. The systemd watchdog * will restart the service if the notification is not received. */ public class WatchdogNotifierTask implements Runnable { private static final String SYSTEMD_SO = "systemd"; private static final String WATCHDOG_READY = "WATCHDOG=1"; @Override public void run() { try { int returnCode = SystemD.INSTANCE.sd_notify(0, WATCHDOG_READY); if (returnCode < 0) { Log.MAIN_LOG.error( "Systemd watchdog returned a negative error code: " + Integer.toString(returnCode)); } else { Log.MAIN_LOG.debug("Successfully updated systemd watchdog."); } } catch (Exception e) { Log.MAIN_LOG.error("calling sd_notify native code failed with exception: ", e); } } /** * This is a linux-specific interface to load the systemd shared library and call the sd_notify * function. Should we need other systemd functionality, it can be loaded here. It uses JNA for * native library calls. * */ interface SystemD extends Library { SystemD INSTANCE = (SystemD) Native.loadLibrary(SYSTEMD_SO, SystemD.class); int sd_notify(int unset_environment, String state); } } 

任何人都有任何想法为什么systemd-notify有时不起作用?

这实际上是几个系统协议中的长期问题,而不仅仅是systemd-notify所说的就绪通知协议。 将事物直接发送到systemd自己的日志的协议也有这个问题。

两种协议都试图通过读取/proc/ client-process-id /*内容来找出有关发送,客户端,进程的内容。 不幸的是, systemd-notify是一个短命的程序,一旦将消息发送到服务器就会退出。 因此,读取/proc/ client-process-id /*不会产生有关服务器所需客户端的信息。 特别地,服务器不能确定客户端所属的(systemd)控制组,从而确定哪个服务单元控制它,从而确定它是否是允许发送准备就绪通知消息的进程。

正如您所发现的那样,在您的实际守护进程中调用库例程,而不是分支短暂的子进程来运行systemd-notify避免了这个问题,因为当然您的发送通知后不会立即退出。 但是,请注意,如果您在退出守护程序之前立即发出准备就绪通知(具有讽刺意味的是,为了通知全世界他们正在终止,有些dæmons会这样做),即使在进程中,您也会遇到同样的问题库函数。

顺便说一下,没有必要将systemd库函数作为本机代码调用以便说出这个协议。 (并且使用库函数可以获得正确说出此协议的优势,即使systemd不在服务器端 – 系统库函数失败。)用Java说话并不是一个硬协议, systemd手册页描述了协议。 您查看环境变量,打开数据报套接字,使用变量的值作为要发送的套接字的名称,发送单个数据报消息,然后关闭套接字。 Java就是这样的。 ☺

进一步阅读

  • Jonathan de Boyne Pollard(2016年)。 “在提取客户端凭据时使用同步协议” 。 Unixdæmons的准备协议问题 。 经常给出答案。
  • Oracle公司(2005年)。 环境变量 。 Java教程。
  • sd_notifysystemd手册 。 Freedesktop.org。
  • Java的UNIX套接字实现?