web-dev-qa-db-ja.com

実行されているcronジョブの重複を防ぐ

毎分実行するようにcronジョブをスケジュールしましたが、スクリプトが終了するまでに1分以上かかる場合があり、ジョブが互いに「スタック」し始めたくありません。これは同時実行の問題だと思います。つまり、スクリプトの実行は相互に排他的である必要があります。

問題を解決するために、スクリプトに特定のファイル( "lockfile.txt")の存在を検索させ、存在する場合は終了し、存在しない場合はtouchを終了させました。しかし、これはかなりひどいセマフォです!知っておくべきベストプラクティスはありますか?代わりにデーモンを作成する必要がありますか?

97
Tom

この機能を自動化するいくつかのプログラムがあり、これを自分で行うことによる煩わしさと潜在的なバグを取り除き、裏側でflockを使用することで古くなったロックの問題を回避します(これはタッチを使用している場合はリスクです) 。私は過去にlockrunlckdoを使用しましたが、今はflock(1)(util-linuxの新しいバージョン)があり、これは素晴らしいことです。使い方はとても簡単です。

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

シェルでの最良の方法は flock(1) を使用することです

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

実際、flock -nlckdo *の代わりに使用できるため、カーネル開発者のコ​​ードを使用します。

wombleの例 に基づいて、次のように記述します。

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

ところで、コードを見ると、flocklockrunlckdoのすべてがまったく同じことをしているので、どれが最も簡単に利用できるかが問題です。

23
Amir

前回の実行が完了するまでスクリプトを待機させるかどうかを指定していません。 「ジョブが互いに「積み重なる」ことを開始したくない」ということで、すでに実行されている場合にスクリプトを終了することを示唆していると思いますが、

したがって、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

ロックファイルを使用できます。スクリプトの開始時にこのファイルを作成し、終了時に削除します。スクリプトは、メインルーチンを実行する前に、ロックファイルが存在するかどうかを確認し、それに応じて続行する必要があります。

ロックファイルは、UNIXシステムのinitscriptや他の多くのアプリケーションやユーティリティで使用されます。

2
Born To Ride

Systemdがリリースされたので、Linuxシステムには別のスケジューリングメカニズムがあります。

systemd.timer

/etc/systemd/system/myjob.serviceまたは~/.config/systemd/user/myjob.service

[Service]
ExecStart=/usr/local/bin/myjob

/etc/systemd/system/myjob.timerまたは~/.config/systemd/user/myjob.timer

[Timer]
OnCalendar=minutely

[Install]
WantedBy=timers.target

タイマーが次にアクティブになるときにサービスユニットがすでにアクティブになっている場合、サービスの別のインスタンスnotが開始されます。

別の方法では、起動時に1回、各実行が終了してから1分後にジョブを開始します。

[Timer]
OnBootSec=1m
OnUnitInactiveSec=1m 

[Install]
WantedBy=timers.target
1
Amir

これは、あなたが間違ったことをしている兆候かもしれません。ジョブがそのように頻繁に実行される場合は、ジョブのクローンを解除してデーモンスタイルのプログラムにすることを検討する必要があります。

以前のインスタンスがまだ実行されている場合は、cronデーモンがジョブを呼び出してはなりません。私は1つのcronデーモン dcron の開発者です。 Vixie cronや他のデーモンがこれをどのように処理するのか私にはわかりません。

1
dubiousjim

run-one コマンドを使用することをお勧めします-ロックを処理するよりもはるかに簡単です。ドキュメントから:

run-oneは、引数の一意のセットを使用して、コマンドの一意のインスタンスを1つだけ実行するラッパースクリプトです。これは、一度に実行するコピーを1つだけにする場合に、cronjobsで役立ちます。

run-this-oneは、pgrepとkillを使用してユーザーが所有している実行中のプロセスを見つけて強制終了することを除いて、run-oneとまったく同じです。ターゲットのコマンドと引数を一致させます。 run-this-oneは、一致するすべてのプロセスが停止するまで、一致するプロセスを強制終了しようとするときにブロックされることに注意してください。

run-one-constantlyは、COMMANDが終了するたびに「COMMAND [ARGS]」を再生成することを除いて、run-oneとまったく同じように動作します(ゼロまたはゼロ以外) 。

keep-one-runningは、run-one-constantlyのエイリアスです。

run-one-until-successは、COMMANDが正常に終了するまで「COMMAND [ARGS]」を再生成することを除いて、run-one-constantlyとまったく同じように動作します(つまり、ゼロを終了します)。

run-one-until-failureは、COMMANDが失敗して終了するまで「COMMAND [ARGS]」を再生成することを除いて、run-one-constantlyとまったく同じように動作します(つまり、 、ゼロ以外で終了します)。

1
Yurik

重複するcronが実行されているなどの問題を解決するために1つのjarを作成しましたJavaまたはShellcron。Duplicate.CloseSessions( "Demo.jar")にcron名を渡すだけで、これは検索して終了しますcurrentを除く、このcronのpidを存在します。これを実行するメソッドを実装しました。Stringproname = 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+" ";    
                }
            }

そして、もう一度Shellコマンドでkillid文字列を殺します

0
Sachin Patil

@Philip Reynoldsの回答は、ロックを取得せずに5秒待機した後にコードの実行を開始します。以下 Flockが機能していないようです 私は@Philip Reynoldsの回答を変更しました

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

そのため、コードが同時に実行されることはありません。代わりに、5秒待機した後、それまでにロックを取得していなかった場合、プロセスは1で終了します。

0
user__42