我很惊讶我无法找到更多关于这个的信息,但遗憾的是,我仍然找不到答案。我们最近转换为 AWS,将我们简单的网站迁移到更强大和可靠的系统。目前让我感到困惑的是在分布式系统上管理 cron 作业,当该 cron 作业被推送到环境中的每个实例时。
这是用例:
背景
设置
我们正在运行传统的 LAMP 堆栈。可能是第一个问题,但这是我们遇到的问题。
数据库表
table1
- id int(11)
- start date
- interval int(11) (number of seconds)
table2
- id int(11)
- table1_id int(11)
- sent datetime
目标
目标是脚本每天运行一次并检查以下内容:
- 当前日期已超过
table1.start
table1.start
<当前日期table1.interval
> 0- 今天恰好是一个完整的间隔(如果间隔是 7 天 [以秒为单位] 并且是第 6 天,那么将会失败)
table2
中没有条目使得table2.sent
是今天并且table2.table1_id
与之前的检查匹配。
如果所有这些检查都通过,我们会为每个具有间隔的 table1 插入一个条目到 table2 中。这也意味着我们根据表 2 中的数据发送电子邮件。
问题
本质上,我们有两个查询,由上述 block 表示。问题在于,在分布式系统上,每个实例将同时运行 cron(或彼此相差几毫秒)。没有“事务”的概念,因此如果在其他实例运行第一个查询之前没有机会插入到 table2
中,每个实例都会发送一封电子邮件。
解决方案???
我对此进行了大量研究,但我提出的唯一可能的解决方案详述如下:
Cron 实例
设置一个负责运行 cron 作业的独立实例。虽然这肯定会(据我所知)有效,但对于不是非常昂贵且每天最多只需要运行一次的工作来说,这是非常昂贵的。
PHP 调度器
设置 cron 以定期运行充当调度程序的 PHP 脚本。在研究表明这对我们有限的时间和金钱来说是最简单的之后,这就是我们要走的路线。我遇到的问题是,这似乎只是将并发问题从消耗作业转移到调度作业。您何时安排作业,以便不会从运行 cron 的每个实例同时安排多个作业?
这个方法看起来也很“笨拙”(借用我 friend 的一句话),我不得不同意。
交易
虽然我对此进行了相当多的研究,但并发总是通过数据库上的原子事务来解决,但据我所知,使用 LAMP 实现这一点并不容易。但也许我错了,我会很高兴被证明是这样。
最后
所以如果有人能帮我解决这个问题,我将不胜感激。也许我的谷歌搜索技能已经生疏了,但我无法想象我是唯一一个遭受这项(可能很简单)任务的人。
最佳答案
我遇到了类似的问题。而且我还有必须每分钟运行一次的 cron 作业,但仅在单个主机上运行
我用这个 hack 解决了这个问题,它运行 amazon autoscaling 工具来查明运行它的盒子是否是这个 auto scaling 组中最后一个实例化的盒子。这显然假设您使用自动缩放,并且主机名包含实例 ID。
#!/usr/bin/env ruby
AWS_AUTO_SCALING_HOME='/opt/AutoScaling'
AWS_AUTO_SCALING_URL='https://autoscaling.eu-west-1.amazonaws.com'
MY_GROUP = 'Production'
@cmd_out = `bash -c 'AWS_AUTO_SCALING_HOME=#{ AWS_AUTO_SCALING_HOME }\
AWS_AUTO_SCALING_URL=#{ AWS_AUTO_SCALING_URL }\
#{ AWS_AUTO_SCALING_HOME }/bin/as-describe-auto-scaling-instances'`
raise "Output empty, should not happen!" if @cmd_out.empty?
@lines = @cmd_out.split(/\r?\n/)
@last = @lines.select {|l| l.match MY_GROUP }.reverse.
detect { |l| l =~ /^INSTANCE\s+\S+\s+\S+\s+\S+\s+InService\s+HEALTHY/ }
raise "No suitable host in autoscaling group!" unless @last
@last_host = @last.match(/^INSTANCE\s+(\S+)/)[1]
@hostname = `hostname`
if @hostname.index(@last_host)
puts "It's me!"
exit(0)
else
puts "Someone else will do it!"
exit(1)
end
将其保存为/usr/bin/lastonly,然后在我的 cron 作业中:
lastonly && do_my_stuff
显然它并不完美,但它对我有用,而且很简单!
关于php - AWS 上的 Cron(或一般的分布式系统),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11513791/