cron スクリプトはキューとして機能しますか、それとも cron のキューとして機能しますか?
質問
誰かがすでにこの問題を解決していると確信しています。おそらく私が答えを教えてくれるGoogleの検索用語を間違っているのかもしれませんが、これが私の状況です。
実行したいスクリプトがありますが、スケジュールされた時間にのみ、一度に 1 つだけ実行したいと考えています。(スクリプトを同時に実行することはできません)
ここで厄介な部分は、必要なデータとスケジュールされた時刻を含む「myhappyschedule」というテーブルがあるとします。このテーブルには同時に複数のスケジュール時間を設定でき、それぞれがこのスクリプトを実行します。したがって、基本的に、スクリプトが起動するたびにキューが必要になり、スクリプトが完了するまですべてのキューが待機する必要があります。(スクリプトの実行に 1 分しかかからない場合もありますが、何分もかかる場合もあります)
私がやろうと考えているのは、myhappyschedule を 5 分ごとにチェックし、スケジュールされたものを集めてキューに入れ、別のスクリプトがキュー内の各「ジョブ」またはオカレンスを順番に実行できるようにするスクリプトを作成することです。これはすべて面倒に聞こえます。
長くなりますが、ユーザーが crontab を編集するのではなく、myhappyschedule でスケジュールを設定できるようにしていると言うべきです。
これについて何ができるでしょうか?ファイルロックとスクリプト呼び出しスクリプト?
解決
列exec_status
をmyhappytable
に追加します(time_started
およびtime_finished
も可能、擬似コードを参照)
次のcronスクリプトをx分ごとに実行します
cronスクリプトの擬似コード:
[create/check pid lock (optional, but see "A potential pitfall" below)]
get number of rows from myhappytable where (exec_status == executing_now)
if it is > 0, exit
begin loop
get one row from myhappytable
where (exec_status == not_yet_run) and (scheduled_time <= now)
order by scheduled_time asc
if no such row, exit
set row exec_status to executing_now (maybe set time_started to now)
execute whatever command the row contains
set row exec_status to completed
(maybe also store the command output/return as well, set time_finished to now)
end loop
[delete pid lock file (complementary to the starting pid lock check)]
この方法では、スクリプトは最初にコマンドが実行されていないかどうかを確認し、指定された瞬間に実行するコマンドがなくなるまで、まだ実行されていないコマンドを最初に実行します。また、データベースにクエリを実行すると、実行されているコマンドを確認できます。
潜在的な落とし穴: cronスクリプトが強制終了された場合、スケジュールされたタスクは<!> quot; executing_now <!> quot;に残ります。状態。それが、最初と最後のpidロックの目的です。cronスクリプトが適切に終了したかどうかを確認します。作成/チェックpidlockの擬似コード:
if exists pidlockfile then
check if process id given in file exists
if not exists then
update myhappytable set exec_status = error_cronscript_died_while_executing_this
where exec_status == executing_now
delete pidlockfile
else (previous instance still running)
exit
endif
endif
create pidlockfile containing cron script process id
他のヒント
スクリプト内でat(1)コマンドを使用して、次回の実行をスケジュールできます。終了する前に、次回の実行時にmyhappyscheduleを確認できます。 cronはまったく必要ありません、本当に。
キューの問題の解決策を調べているときに、この質問に遭遇しました。他の検索者の利益のために、ここに私の解決策があります。
これを、スケジュールどおりにジョブを開始する cron と組み合わせると (同時に実行するようにスケジュールされている場合でも)、あなたが説明した問題も解決します。
問題
- スクリプトのインスタンスは最大 1 つだけ実行する必要があります。
- リクエストをキューに入れて、できるだけ早く処理したいと考えています。
つまり。スクリプトへのパイプラインが必要です。
解決:
任意のスクリプトへのパイプラインを作成します。小さな bash スクリプトを使用して実行します (さらに下にあります)。
スクリプトは次のように呼び出すことができます
./pipeline "<any command and arguments go here>"
例:
./pipeline sleep 10 &
./pipeline shabugabu &
./pipeline single_instance_script some arguments &
./pipeline single_instance_script some other_argumnts &
./pipeline "single_instance_script some yet_other_arguments > output.txt" &
..etc
スクリプトは新しいファイルを作成します 名前付きパイプ コマンドごとに。したがって、上記は名前付きパイプを作成します。 sleep
, shabugabu
, 、 そして single_instance_script
この場合、最初の呼び出しでリーダーが開始され、実行されます。 single_instance_script
と some arguments
引数として。呼び出しが完了すると、リーダーはパイプから次のリクエストを取得し、次のように実行します。 some other_arguments
, 、完了、次を取得など...
このスクリプトは要求プロセスをブロックするため、バックグラウンド ジョブ (最後に &) として呼び出すか、分離されたプロセスとして呼び出します。 at
(at now <<< "./pipeline some_script"
)
#!/bin/bash -Eue
# Using command name as the pipeline name
pipeline=$(basename $(expr "$1" : '\(^[^[:space:]]*\)')).pipe
is_reader=false
function _pipeline_cleanup {
if $is_reader; then
rm -f $pipeline
fi
rm -f $pipeline.lock
exit
}
trap _pipeline_cleanup INT TERM EXIT
# Dispatch/initialization section, critical
lockfile $pipeline.lock
if [[ -p $pipeline ]]
then
echo "$*" > $pipeline
exit
fi
is_reader=true
mkfifo $pipeline
echo "$*" > $pipeline &
rm -f $pipeline.lock
# Reader section
while read command < $pipeline
do
echo "$(date) - Executing $command"
($command) &> /dev/null
done