java:systemd-notify

时间:2015-11-27 20:16:20

标签: java systemd

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

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

使用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,但是想知道在执行该路由之前我是否可以执行简单的配置操作。

2 个答案:

答案 0 :(得分:6)

这是解决问题的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);
}

}

答案 1 :(得分:4)

  

任何人都有任何想法为什么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就是这样的。 ☺

进一步阅读