繁体   English   中英

AWS 上的 Cron(或一般的分布式系统)

[英]Cron on AWS (or distributed systems in general)

我很惊讶我无法找到更多关于这个的信息,但遗憾的是,我仍然找不到答案。 我们最近转换为 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

目标

目标是脚本每天运行一次并检查以下内容:

  1. 当前日期已超过table1.start
  2. table1.start < 当前日期
  3. table1.interval > 0
  4. 今天正好是一个完整的间隔(如果间隔是 7 天 [以秒为单位] 并且是第 6 天,那么将会失败)
  5. table2中没有条目表明table2.sent是今天并且table2.table1_id与之前的检查匹配。

如果所有这些检查都通过了,我们会为每个具有间隔的 table1 插入一个条目到 table2 中。 这也意味着我们根据表 2 中的数据发送电子邮件。

问题

本质上,我们有两个查询,由上述块表示。 问题在于,在分布式系统上,每个实例将同时运行 cron(或彼此相差几毫秒)。 没有“事务”的概念,因此如果在其他实例运行第一个查询之前没有机会插入到table2中,每个实例都会发送一封电子邮件。

解决方案???

我对此进行了大量研究,但我提出的唯一可能的解决方案详述如下:

Cron 实例

设置一个负责运行 cron 作业的独立实例。 虽然这肯定会(据我所知)工作,但对于一项不是非常昂贵且每天最多只需要运行一次的工作来说,这是非常昂贵的。

PHP调度程序

设置 cron 以定期运行充当调度程序的 PHP 脚本。 在研究表明这对我们有限的时间和金钱来说是最简单的之后,这就是我们要走的路线。 我遇到的问题是,这似乎只是将并发问题从消耗作业转移到调度作业。 您何时安排作业,以便不会从运行 cron 的每个实例同时安排多个作业?

这种方法似乎也很“笨拙”(借用我朋友最喜欢的一句话),我不得不同意。

交易

尽管我对此进行了相当多的研究,并发总是通过数据库上的原子事务来解决,但据我所知,使用 LAMP 实现这一点并不容易。 但也许我错了,我会很高兴被证明是这样。

最后

因此,如果有人可以帮助我解决这个问题,我将不胜感激。 也许我的谷歌搜索技能已经生锈了,但我无法想象我是唯一一个遭受这项(可能很简单)任务的人。

我有一个类似的问题。 而且我还有必须每分钟运行一次的 cron 作业,但只在一台主机上运行

我用这个 hack 解决了这个问题,它运行 amazon autoscaling 工具来查明它运行的盒子是否是这个自动缩放组中实例化的最后一个。 这显然假设您使用自动缩放,并且主机名包含实例 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

显然它并不完美,但它对我有用,而且很简单!

查看 Gearman 项目http://www.gearman.org 基本架构是您将拥有一台机器作为作业服务器,所有其他机器都成为服务器的客户端。

您可以在作业服务器上设置 crontab,以将要执行的命令发送到通过 Gearman 连接的所有客户端。 然后,您可以使用 PHP 对您的 cron 作业进行切片和切块,并根据需要深入 Map/Reduce。

这是一个关于概念及其工作原理的很好的教程: http ://www.lornajane.net/posts/2011/Using-Gearman-from-PHP

不要因为立即使用 Gearman 之类的东西而感到沮丧。 分布式 cron 系统可能很复杂,但一旦你了解它,你就会没事的。

FWIW,我们每分钟在亚马逊 EC2 上的 Gearman 工人农场中处理数千个 cron 脚本。 我们非常喜欢它。

您可以使用队列使任务只运行一次。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM