[英]Rails 3.1/rake - datespecific tasks without queues
我想让我的用户可以选择在特定时间(给定的用户)向他们发送其帐户统计信息的每日摘要...。
可以说以下模型:
class DailySummery << ActiveRecord::Base
# attributes:
# send_at
# => 10:00 (hour)
# last_sent_at
# => Time of the last sent summary
end
现在是否有最佳做法,如何通过电子邮件将此帐户摘要发送到特定时间?
目前,我正在运行一个无限的rake任务,该任务将永久检查是否有电子邮件可以发送,并且我想将每日摘要生成并发送到此rake任务中。
我想到可以使用以下伪代码解决此问题:
while true
User.all.each do |u|
u.generate_and_deliver_dailysummery if u.last_sent_at < Time.now - 24.hours
end
sleep 60
end
但是我不确定这是否有一些隐藏的警告...
注意:我不想使用诸如resq或redis之类的队列!
编辑:添加了睡眠(我的脚本中已经有)
编辑:这是一项时间紧迫的服务(通知贸易价格),因此应尽可能快。 这就是为什么我不想使用基于队列或作业的系统的背景。 我使用Monit来管理此rake任务,该任务确实很好。
延迟执行实际上只有两种主要方法。 当站点上的用户访问页面时,您将运行该脚本,该页面效率不高且不完全准确。 或使用某种后台程序,无论是cron作业还是resque / delay作业/等。
尽管使rake进程永久运行的方法可以很好地工作,但它效率不高,因为您要在24/7上对用户进行一次24/7的迭代,例如:
while true
User.where("last_sent_at <= ? OR last_sent_at = ?", 24.hours.ago, nil).each do |u|
u.generate_and_deliver_dailysummery
end
sleep 3600
end
它每小时运行一次,仅拉动需要发送电子邮件的用户,效率更高一些。 最佳实践是使用cronjob,尽管它可以运行您的rake任务。
定期运行任务是cron的目的。 每当使用gem(https://github.com/javan/whenever)时,就可以轻松为您的应用配置cron定义。
随着应用程序的扩展,您可能会发现rake任务花费的时间太长,并且队列在cron计划之外非常有用。 您可以使用cron来控制何时安排交货,但实际上由工作人员池执行交货。
我看到在特定时间执行任务的两种可能性。
后台进程/工作者/ ...
这是您已经完成的。 我重构了您的示例,因为有两个不好的地方。
在您的代码旁边,我看到了另一个问题。 您将如何在生产服务器上管理此后台作业? 如果您不想使用Resque或其他工具,则应考虑以其他方式进行管理。 有Monit和God两者都是过程监视器。
while true
# Check the condition from your database
users = User.where(['last_sent_at < ? OR created_at IS NULL', 24.hours.ago])
# Load by batch of 1000
users.find_each(:batch_size => 1000) do |u|
u.generate_and_deliver_dailysummery
end
sleep 60
end
Cron作业/预定任务/ ...
第二种可能性是递归安排任务,例如每小时或每半小时。 如果我错了,请纠正我,但是您的用户是否真的需要将交货安排在上午10:39? 我认为让他们选择小时就足够了 。
应用此功能,我认为每小时执行一次的工作比每分钟查询数据库的无限任务要好 。 此外, 这确实很容易完成 ,因为您无需进行任何设置。
使用ruby语法可以很好地管理cron任务。 更多信息在这里: 每当
您可以执行此操作,还需要检查发送时间。 因此,从您的伪代码开始并添加到其中:
while true
User.all.each do |u|
if u.last_sent_at < Time.now - 24.hours && Time.now.hour >= u.send_at
u.generate_and_deliver_dailysummery
# the next 2 lines are only needed if "generate_and_deliver_dailysummery" doesn't sent last_sent_at already
u.last_sent_at = Time.now
u.save
end
end
sleep 900
end
我还添加了sleep
因此您不必不必要地锤击数据库。 您可能还想研究将循环限制为仅需要发送给的用户组。 类似于Zachary建议的查询将比您拥有的查询效率更高。
如果您不想使用队列,请考虑延迟的工作(有点像个穷人队列),它确实像您正在执行的耙任务一样运行
它将所有任务存储在作业表中,通常在您添加任务时将其排队以尽快运行,但是您可以重写此命令以将其延迟到特定时间
您可以将DailySummary类转换为DailySummaryJob,并在完成后可以在接下来的几天中重新排队其自身的新实例
您如何更新last_sent_at
属性?
如果您使用
last_sent_at += 24.hours
并使用last_sent_at = Time.now.at_beginning_of_day + send_at
初始化
没关系。
不要使用last_sent_at = Time.now
。 这是因为在实际完成工作时可能会有一些延迟,这会使last_sent_at
属性越来越“延迟”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.