Вопрос: Предотвращение дублирования заданий cron


Я планировал выполнение задания cron каждую минуту, но иногда сценарий занимает больше минуты, чтобы закончить, и я не хочу, чтобы задания запускались «друг над другом». Я предполагаю, что это проблема параллелизма, то есть выполнение скрипта должно быть взаимоисключающим.

Чтобы решить проблему, я заставил скрипт искать существование определенного файла ("lockfile.txt") и выйти, если он существует или touch если это не так. Но это довольно паршивый семафор! Есть ли лучшая практика, о которой я должен знать? Должен ли я написать вместо демона?


79
2017-11-09 11:32


Источник




Ответы:


Есть несколько программ, которые автоматизируют эту функцию, отнимают у вас досаду и потенциальные ошибки, делая это сами, и избегайте проблемы с устаревшим блокированием, используя стаю за кулисами (что является риском, если вы просто используете прикосновение) , Я использовал lockrun а также lckdo в прошлом, но теперь есть flock(1) (в новых версиях util-linux), что отлично. Это очень просто:

* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job

105
2017-11-09 11:57



lckdo будет удален из moreutils, теперь, когда flock (1) находится в util-linux. И этот пакет в основном обязателен в системах Linux, поэтому вы должны быть в состоянии полагаться на его присутствие. Для использования смотрите ниже. - jldugger
Да, стадо - теперь мой предпочтительный вариант. Я даже уточню свой ответ. - womble♦
Кто-нибудь знает разницу между flock -n file command а также flock -n file -c command ? - Nanne
@Nanne, я должен проверить код, чтобы быть уверенным, но моя образованная догадка заключается в том, что -c запускает указанную команду через оболочку (согласно man-странице), в то время как «голый» (не--c) только execс приведенной командой. Помещение чего-то через оболочку позволяет делать вещи, подобные оболочке (например, выполнение нескольких команд, разделенных ; или &&), но также открывает вам атаки на расширение оболочки, если вы используете ненадежный ввод. - womble♦
Это был аргумент (гипотетический) frequent_cron_job команда, которая пыталась показать, что она запускается каждую минуту. Я удалил его с тех пор, как он добавил ничего полезного и вызвал путаницу (твой, если никто еще не на протяжении многих лет). - womble♦


Лучший способ в оболочке - использовать стадо (1)

(
  flock -x -w 5 99
  ## Do your stuff here
) 99>/path/to/my.lock

27
2017-11-09 11:45



Я не могу выдвинуть непростое использование перенаправления fd. Это просто слишком потрясающе. - womble♦
Не анализирует меня в Bash или ZSH, необходимо устранить пространство между 99 а также > так что, это 99> /... - Kyle Brandt♦
@Javier: Не значит, что это не сложно и тайно, просто документированный, сложный и тайный. - womble♦
что произойдет, если вы перезагрузитесь, пока это запущено, или каким-то образом процесс будет убит? Неужели это будет заблокировано навсегда? - Alex R
Я понимаю, что эта структура создает эксклюзивный замок, но я не понимаю, как это делается. Какова функция «99» в этом ответе? Кто-нибудь хочет объяснить это, пожалуйста? Благодаря! - Asciiom


На самом деле, flock -n могут использоваться вместо lckdo*, поэтому вы будете использовать код от разработчиков ядра.

Основываясь на пример womble, вы должны написать что-то вроде:

* * * * * flock -n /some/lockfile command_to_run_every_minute

Кстати, глядя на код, все flock, lockrun, а также lckdo сделайте то же самое, так что это просто вопрос, который наиболее легко доступен вам.

* Поскольку, учитывая мою репутацию на момент написания, я не могу ни редактировать, ни комментировать предыдущие ответы, я должен написать это как отдельный ответ.


21
2017-11-19 22:43





Вы можете использовать файл блокировки. Создайте этот файл при запуске скрипта и удалите его, когда он закончится. Скрипт, прежде чем он выполнит свою основную процедуру, должен проверить, существует ли файл блокировки и действовать соответствующим образом.

Lockfiles используются initscripts и многими другими приложениями и утилитами в Unix-системах.


2
2017-11-09 11:36



это только который я когда-либо видел, реализовал лично. Я использую в соответствии с предложением сопровождающего в качестве зеркала для проекта OSS - warren


Это также может быть признаком того, что вы поступаете неправильно. Если ваши задания работают так близко и часто, возможно, вам стоит подумать об отказе от нее и превратить ее в программу типа демона.


1
2017-11-09 11:45



Я от всей души не согласен с этим. Если у вас есть что-то, что нужно периодически запускать, делая его демоном, это решение «кувалда для ореха». Использование файла блокировки для предотвращения несчастных случаев - это совершенно разумное решение, с которым я никогда не сталкивался. - womble♦
@womble Я согласен; но мне нравится разбивать орехи кувалдами! :-) - wzzrd


Вы указали havent, если хотите, чтобы сценарий дождался завершения предыдущего прогона. «Я не хочу, чтобы задания запускались« друг над другом », я полагаю, вы подразумеваете, что вы хотите, чтобы скрипт вышел, если он уже запущен,

Итак, если вы не хотите зависеть от lckdo или подобного, вы можете сделать это:


PIDFILE=/tmp/`basename $0`.pid

if [ -f $PIDFILE ]; then
  if ps -p `cat $PIDFILE` > /dev/null 2>&1; then
      echo "$0 already running!"
      exit
  fi
fi
echo $$ > $PIDFILE

trap 'rm -f "$PIDFILE" >/dev/null 2>&1' EXIT HUP KILL INT QUIT TERM

# do the work


1
2017-11-09 14:33



Спасибо, что ваш пример полезен - я хочу, чтобы скрипт вышел, если он уже запущен. Благодарим за упоминание ickdo - Кажется, это трюк. - Tom


Ваш демон cron не должен вызывать задания, если предыдущие экземпляры их все еще запущены. Я разработчик одного cron-демона dcron, и мы специально пытаемся это предотвратить. Я не знаю, как справиться с этим Vixie cron или другими демонами.


1
2018-02-17 15:59





Я бы рекомендовал использовать вводные один команда - намного проще, чем иметь дело с замками. Из документов:

вводные один это сценарий оболочки, который содержит не более одного уникального экземпляра некоторой команды с уникальным набором аргументов. Это часто полезно для cronjob, когда вы хотите, чтобы одновременно выполнялось не более одной копии.

вводное это-один точно так же, как run-one, за исключением того, что он будет использовать pgrep и kill для поиска и уничтожения любых запущенных процессов, принадлежащих пользователю и сопоставление целевых команд и аргументов. Обратите внимание, что run-this-one будет блокироваться при попытке убить соответствующие процессы, пока все совпадения процессы мертвы.

вводные один-постоянно работает точно так же, как и run-one, за исключением того, что он respawns "COMMAND [ARGS]" в любое время, когда COMMAND выходит (ноль или ненулевое значение).

держать-один выполняющихся является псевдонимом для run-one-постоянно.

не вводные один-до-успеха работает точно так же, как run-one-постоянно, кроме что он обновляет команду «COMMAND [ARGS]» до тех пор, пока COMMAND не выйдет успешно (то есть, завершает ноль).

не вводные один-до-отказа работает точно так же, как run-one-постоянно, кроме что он возрождает «COMMAND [ARGS]», пока COMMAND не выйдет с ошибкой (т. е. выходит за пределы нуля).


1
2017-10-02 21:53





Я создал одну банку для решения такой проблемы, как работает дубликат кронов, может быть java или shell cron. Просто передайте имя cron в Duplicates.CloseSessions ("Demo.jar"), это будет искать и убивать существующий pid для этого cron, кроме текущего. Я реализовал метод для этого. String proname = ManagementFactory.getRuntimeMXBean (). GetName ();                 String pid = proname.split ("@") [0];                 System.out.println («Текущий PID:» + pid);

            Process proc = Runtime.getRuntime().exec(new String[]{"bash","-c"," ps aux | grep "+cronname+" | awk '{print $2}' "});

            BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            String s = null;
            String killid="";

            while ((s = stdInput.readLine()) != null ) {                                        
                if(s.equals(pid)==false)
                {
                    killid=killid+s+" ";    
                }
            }

И затем убить killid строку с помощью команды shell


0
2017-12-28 08:36



Я не думаю, что это действительно отвечает на вопрос. - kasperd