cron脚本作为队列还是cron的队列?

8
我猜想已经有人解决了这个问题,也许我用的是错误的搜索词,无法在谷歌上找到答案,但是这是我的情况。
我有一个脚本想要运行,但我希望它只在预定时间运行,并且每次只能运行一个。(不能同时运行脚本)
现在棘手的部分是,假设我有一个名为“myhappyschedule”的表格,其中包含我需要的数据和预定时间。即使在同一时间,该表格可以有多个预定时间,每个时间都会运行此脚本。因此,实质上,我需要一个队列来记录脚本触发的每个时间,并且所有队列中的时间都需要等待前面的时间完成后才能开始。(有时,脚本执行只需要一分钟,有时则需要很多分钟)
我正在考虑制作一个脚本,每5分钟检查myhappyschedule并收集预定的时间,将它们放入队列中,另一个脚本可以按顺序执行队列中的每个“工作”或事件。这些听起来都很混乱。
为了让这更长一些 - 我应该说我允许用户在myhappyschedule中安排事务,而不是编辑crontab。
对此有什么解决办法吗?文件锁和脚本调用脚本?
3个回答

4

myhappytable中添加一列exec_status(也许还有time_startedtime_finished,请参见伪代码)。

每x分钟运行以下cron脚本:

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脚本被终止,一个计划任务将保持在“executing_now”状态。这就是开头和结尾处pid锁的作用:查看cron脚本是否正常终止。创建/检查pid锁的伪代码如下:
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

1
这是伪代码,它帮助我避免了在作业排队方面走上一条不可维护的错误道路。非常感谢Piskvor。 - bouvard

2

您可以在脚本中使用at(1)命令来安排下一次运行。在退出之前,它可以检查myhappyschedule以获取下一次运行时间。实际上,您根本不需要cron。


嗯...这是我没有考虑到的事情。我想知道如果脚本正在运行时是否会错过预定的时间。比如它在早上8点执行,然后运行了10分钟,错过了在它运行期间开始和结束时间之间的任何预定时间。 - user30413
好主意。您可以在编辑myhappyschedule的任何过程中操纵atq。我得想一想这个午餐。 - Steven Huwig
我认为,如果你的脚本在运行结束时检查myhappyschedule,并从中弹出最早计划的开始时间来确定下一次运行时的时间,它将按预期工作。关键是要选择仍在myhappyschedule中的最早时间。 - Steven Huwig

0
我在研究排队问题的解决方案时遇到了这个问题。为了让其他人受益,这是我的解决方案。
将其与一个cron结合起来,按计划启动作业(即使它们计划在同一时间运行),这也解决了您描述的问题。
问题:
  • 脚本最多只能运行一个实例。
  • 我们希望将请求排队,以尽可能快地处理它们。

即,我们需要一个管道到脚本。

解决方案:


创建一个管道到任何脚本。使用小型 Bash 脚本实现(以下进一步说明)。

该脚本可以被调用为
./pipeline "<在此输入任何命令和参数>"

示例:

./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

该脚本为每个命令创建一个新的命名管道。因此,上述代码将创建命名管道:sleepshabugabusingle_instance_script

在这种情况下,初始调用将启动一个读取器并使用一些参数作为参数运行single_instance_script。一旦调用完成,读取器将从管道中抓取下一个请求并使用一些其他参数执行,完成后再抓取下一个等等...

该脚本将阻塞请求进程,因此请将其作为后台作业(在结尾处加上&)或使用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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接