java:systemd-notify 中的看门狗超时不一致
Posted
技术标签:
【中文标题】java:systemd-notify 中的看门狗超时不一致【英文标题】:java: inconsistent watchdog timeout in systemd-notify 【发布时间】:2015-11-27 20:16:19 【问题描述】:我的 java 应用程序安装在 OpenSUSE 13.2 操作系统上,并且我使用 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,但想知道在走这条路线之前是否可以做一个简单的配置。
【问题讨论】:
您是否尝试过使用JNI 调用sd_notify(3)?因此,您可以更准确地检查呼叫状态。我想Java守护程序和systemd之间的调用存在一些问题。此外,我会在ProcessBuilder.start()
之前立即记录一条消息,并在systemd-notify
上使用日志记录外壳包装器,以确保及时执行子进程的调用并且没有任何不可预测的延迟
我对 CentOS7.0 (systemd 208) 也有类似的问题。我有同样的 2 分钟看门狗时间,今天它失败了(似乎是随机的)。就我而言,我每秒直接调用一次sd_notify()
。我没有任何迹象表明发送通知的过程完全停止了。
我最终为此使用了 JNA,从那以后它就一直坚如磐石。我将在下面的答案中发布代码。
【参考方案1】:
这是解决问题的 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);
【讨论】:
这个超级有用,我扩展了一下:gist.github.com/juur/048cc3d0554953b717e9c6867970f30e【参考方案2】:有人知道为什么
systemd-notify
有时不起作用吗?
这实际上是几个 systemd 协议中长期存在的问题,而不仅仅是systemd-notify
所说的就绪通知协议。直接发送东西到systemd自己的日志的协议也有这个问题。
两种协议都试图通过读取/proc/<i>client-process-id</i>/*
中的内容来找出有关发送、客户端、进程的信息。不幸的是,systemd-notify
是一个短暂的程序,一旦将消息发送到服务器就会退出。因此读取/proc/<i>client-process-id</i>/*
不会产生服务器需要的有关客户端的信息。特别是,服务器无法确定客户端属于哪个(systemd)控制组,从而无法确定哪个服务单元控制它,从而确定它是否是允许发送就绪通知消息的进程。
正如你所发现的,在你的实际守护进程中调用一个库例程,而不是派生一个短暂的子进程来运行systemd-notify
可以避免这个问题,因为当然你的守护进程在发送后不会立即退出通知。但是请注意,如果您在退出守护程序之前立即发出准备就绪通知(具有讽刺意味的是,有些守护程序是为了通知世界它们正在终止),即使在进程中,您也会遇到同样的问题库函数。
顺便说一句,无需将 systemd 库函数作为本机代码调用即可使用此协议。 (并且 not 使用库函数可以获得正确使用该协议的优势,即使 systemd 不在它的服务器端——这是 systemd 库函数的失败。)它不是一个硬协议用 Java 说话,systemd 手册页描述了该协议。您查看一个环境变量,打开一个数据报套接字,使用该变量的值作为要发送到的套接字的名称,发送一条数据报消息,然后关闭该套接字。 Java 能够做到这一点。 ☺
进一步阅读
乔纳森·德·博因·波拉德 (2016)。 "using a synchronous protocol when pulling client credentials"。 Unix 守护进程的就绪协议问题。经常给出的答案。 甲骨文公司 (2005)。 Environment variables。 Java 教程。sd_notify
。 系统手册。 Freedesktop.org。
UNIX socket implementation for Java?
【讨论】:
您查看一个环境变量,打开一个数据报套接字,使用该变量的值作为要发送到的套接字的名称,发送单个数据报消息,然后关闭该套接字。 Java 可以做到这一点。 听起来像是一个很好的开源库! github.com/faljse/SDNotify,我引用:“Notify 协议使用数据报 unix 套接字,这些套接字无法通过 Java 访问;因此 SDNotify 包含套接字 API 的 JNA 包装器。”因此,Java 无法单独做到这一点。 :'(以上是关于java:systemd-notify 中的看门狗超时不一致的主要内容,如果未能解决你的问题,请参考以下文章