java: несогласованный тайм-аут сторожевого таймера в systemd-notify

Мое приложение Java устанавливается в ОС OpenSUSE 13.2, и я использую systemd для управления процессом. (systemd версия 210)

Я хотел бы воспользоваться функцией сторожевого таймера systemd с помощью systemd-notify. Однако я замечаю, что приложение перезапускается из-за несогласованных тайм-аутов сторожевого таймера.

При WatchdogSec = 120 и приложении, настроенном на вызов systemd-notify каждые 60 секунд, я наблюдаю перезапуски в среднем каждые 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, но хотел бы знать, есть ли простая вещь с конфигурацией, которую я могу сделать, прежде чем идти по этому маршруту.


person Kyle Fransham    schedule 27.11.2015    source источник
comment
Вы пытались использовать вызов JNI для sd_notify (3)? Таким образом, вы могли более точно проверять статус звонка. Я предполагаю, что есть некоторые проблемы с вызовом между демоном Java и systemd. Также я бы поместил сообщение в журнал непосредственно перед ProcessBuilder.start() и использовал оболочку оболочки журналирования над systemd-notify, чтобы убедиться, что вызов подпроцесса выполняется точно во время и нет никаких непредсказуемых задержек.   -  person user3159253    schedule 28.11.2015
comment
У меня аналогичная проблема с CentOS7.0 (systemd 208). У меня такой же сторожевой таймер 2 минуты, и сегодня он отказал (по-видимому, случайно). В моем случае я вызываю sd_notify() напрямую раз в секунду. У меня нет никаких указаний на то, что процесс отправки уведомлений вообще был остановлен.   -  person Mark Lakata    schedule 28.04.2016
comment
В итоге я использовал для этого JNA, и с тех пор он работает как скала. Я отправлю код в ответ ниже.   -  person Kyle Fransham    schedule 29.04.2016


Ответы (2)


Вот код 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);
}

}
person Kyle Fransham    schedule 29.04.2016
comment
Это было очень полезно, я немного расширил его: gist.github.com/juur/048cc3d0554953b717e9c6867970f > - person Ian; 06.11.2018

У кого-нибудь есть идеи, почему systemd-notify иногда просто не работает?

На самом деле это давняя проблема в нескольких протоколах systemd, а не только в протоколе уведомления о готовности, о котором говорит systemd-notify. Протокол для отправки вещей непосредственно в собственный журнал systemd также имеет эту проблему.

Оба протокола пытаются узнать информацию об отправляющем процессе на стороне клиента, считывая данные из /proc/client-process-id/*. К сожалению, systemd-notify - это недолговечная программа, которая завершается сразу после отправки сообщения на сервер. Таким образом, чтение /proc/client-process-id/* не дает информации о клиентской стороне, которая нужна серверу. В частности, сервер не может определить, к какой (systemd) группе управления принадлежит клиентский конец, и, таким образом, определить, какой сервисный блок управляет им, и, таким образом, определить, разрешено ли этому процессу отправлять сообщения с уведомлением о готовности.

Как вы обнаружили, вызов внутрипроцессной библиотечной подпрограммы в вашем реальном dmon вместо разветвления недолговечного дочернего процесса для запуска systemd-notify позволяет избежать этой проблемы, потому что, конечно, ваш dmon не завершает работу сразу после отправки уведомления. Однако имейте в виду, что если вы отправляете уведомление о готовности непосредственно перед выходом из своего демона (как, по иронии судьбы, некоторые демоны делают, чтобы уведомить мир о своем завершении), вы столкнетесь с этой же проблемой даже во время выполнения. библиотечная функция.

Между прочим, нет необходимости вызывать функцию библиотеки systemd как собственный код, чтобы использовать этот протокол. (И отсутствие использования библиотечной функции дает вам преимущество правильного использования этого протокола, даже если systemd не находится на стороне сервера, это является ошибкой библиотечной функции systemd.) Это не сложный протокол для говорите на Java, а страница руководства systemd описывает протокол. Вы смотрите на переменную среды, открываете сокет дейтаграммы, используете значение переменной в качестве имени сокета для отправки, отправляете одно сообщение дейтаграммы, а затем закрываете сокет. Java на это способна. ☺

дальнейшее чтение

person JdeBP    schedule 02.05.2016
comment
Вы смотрите на переменную среды, открываете сокет дейтаграммы, используете значение переменной в качестве имени сокета для отправки, отправляете одно сообщение дейтаграммы, а затем закрываете сокет. Java на это способна. Похоже, хорошая библиотека с открытым исходным кодом для написания! - person Kyle Fransham; 03.05.2016
comment
github.com/faljse/SDNotify, цитирую: протокол Notify использует сокеты unix дейтаграмм, которые не являются доступно через Java; Поэтому SDNotify включает JNA-оболочку API сокета. Так что Java не способна на это одна. : '( - person zenbeni; 11.04.2017