在某个时间点执行代码

时间:2016-08-18 03:34:10

标签: python flask

我正在创建一个由python flask制作的网络应用程序..我想要的是,如果我创建了发票,它会在续订日期自动发送电子邮件,以通知发票已续订等等。请注意更新每3个月发生一次代码如下:

def some_method(): // An API Enpoint that adds an invoice from a request
    // Syntax that inserts the details to the database
    // Let's assume that the the start of the renewal_date is December 15, 2016

如何实现自动电子邮件执行?没有在后端施加太大的压力?因为我猜测如果有300张发票,那么服务器可能会有压力

3 个答案:

答案 0 :(得分:2)

"自己写。"不,真的。

保留要发送的发票的数据库表。每张发票都有一个状态(值pendingsentpaid,...)和发票日期(可能在将来)。使用cron或类似信息定期运行(例如1 /小时,1 /天)一个程序,该程序查询表中发票日期/时间已到达/已通过的待处理发票,但尚未发送任何发票。此开票流程会发送发票,更新状态并完成。此Invoicer实用程序不会成为Flask Web应用程序的组成部分,但可以作为支持程序使用它。

为什么呢?这是一种简单直接的方法。它将发票代码保存在Python中,接近您选择的应用程序语言和数据库。它不需要太多的外部系统或中间件的偏移。使用与编写应用程序本身相同的数据库,查询和技能,可以直接调试和监控。简单,直接,可靠,完成。什么不爱?

现在,我完全理解"自己写一下"推荐与典型的"买不建立#34;教义。但我尝试了所有主要的替代方案,例如cronCelery;我使用创收网络应用程序的经验表明,他们不是几百个长期发票活动的方式。

TL; DR - 为什么不是Cron和Celery?

cron及其后期等价物(例如launchdHeroku Scheduler)会产生重复发生的事件。每隔10分钟,每小时,每天一次,每隔一个星期二凌晨3:10 UTC。他们通常不会在未来的某个时间和日期X解决这个问题""问题,但它们非常适合定期工作。

现在最后一句话并非严格属实。它描述了cron及其部分替代品。但是,即使是传统的Unix也会将at作为旁边的cron,而一些cron后续(例如launchdsystemd)捆绑重复和将来的事件一起安排(以及其他厨房用具和众所周知的水槽)。 Evens所以,有一些问题:

  1. 您依赖外部调度系统。这意味着如果出现问题,学习,监控和调试的另一个界面。这些系统级调度程序与您的Python应用程序之间存在显着的阻抗不匹配。即使您在X时刻农场外运行事件,"您仍然需要编写Python代码来发送发票并在业务工作流程中正确移动它。
  2. 这些系统非常适合少数事件,通常缺少能够直接查看,修改,监控或调试数百个未完成事件的界面。在预定事件的海洋中调试生产应用程序错误是悲伤。您正在谈论向此外部系统提交300多个待处理事件。您还必须考虑如何监控和调试该用途。
  3. 这些调度程序专为"常规"而设计。不是"高价值"或"高度可用"操作。如果只是一个问题,如果安排了一个事件,那么你会停机(计划的还是计划外的)?如果在系统重新启动之前事件时间过去了,那么呢?大多数类似cron的调度程序缺乏强有力的处理和错过"事件或"尽早制作它们。"从技术角度来说,这可能是一个让人失望的人。"说事件触发了汇款 - 或者在您的情况下,发票发票。您有数百张发票,发出这些发票可能是关键业务。系统级调度程序与您的操作需求之间的能力差距可能非常痛苦,特别是在您扩展时。
  4. 好的,那些将这些事件推入像Celery这样的外部事件调度程序呢?这是一个很多更好的主意。 Celery旨在运行大量的应用程序事件。它支持各种后端引擎(例如RabbitMQ),这些引擎在实践中经过验证,可以处理成千上万的数以千计的事件,并且它具有用户界面选项,可以帮助处理事件众多事件。到现在为止还挺好!但是:

    1. 您会发现自己正在处理安装,配置和操作外部中间件(例如RabbitMQ)的复杂性。这项工作产生了非常高的可实现规模,但启动和运营成本是真实的。即使您将大部分资源投入到像Heroku这样的云服务中也是如此。
    2. 更重要的是,作为近期活动的调度员,Celery并不是一个长期等待时间调度员的理想选择。在制作过程中,我发现了很长一段时间的严重问题"事件(一个月发布,或者将来三个月发布)。虽然这些问题并不相同,但就像cron等一样,Celery长投掷事件与正常的应用更新和重启周期非常相似。这取决于环境,但发生在像Heroku这样的流行云服务上。
    3. 芹菜问题并非完全无法解决或致命,但长时间延迟的事件并不是一样的好消息!哇!芹菜让一切都变得更好!"你为成群的近期事件所获得的魔力。你必须成为Celery,RabbitMQ等工程师和看护人。只需安排几百张发票,这是一个很高的价格和很多工作。

      总结:虽然未来的发票安排似乎可以解决问题,但在实践中,将该功能保留在您的主应用程序代码中(而不是直接在您的Flask Web应用程序中,它会更容易,更快速,更强大)作为一个相关的实用程序),并且只是提出让我每天运行N次"低级别的tickler到系统级作业调度程序。

答案 1 :(得分:1)

您可以在Linux中使用crontab,语法如下所示

crontab -e
1 2 3 4 5 /path/to/command arg1 arg2

或许您可以查看Celery,我认为它是处理任务队列的好工具,您可以在这里找到有用的东西。celery.schedules

修改

Schedule Tasks on Linux Using Crontab

HowTo: Add Jobs To cron Under Linux or UNIX?

How to Schedule Tasks on Linux: An Introduction to Crontab Files

答案 2 :(得分:0)

如果我理解正确,您需要在生成发票时获取日期,然后再添加3个月(90天)。您可以在Python中使用datetime.timedelta(days=90)。请看一下:Adding 5 days to a date in Python

从那里,你理论上可以生成一个Threading.timer()的线程(如下所示:Python - Start a Function at Given Time),但我建议不要在这部分使用Python,因为你提到,它会给服务器带来不必要的压力(更不用说服务器出现故障,你失去了所有的日程安排)。

选项A(为每张发票安排任务)

使用操作系统将来安排任务会更好。如果你的后端是基于Linux的,那么Cron应该可以正常工作。看看这个想法:How to setup cron to run a file just once at a specific time in future?。就个人而言,我喜欢this answer,它建议在/etc/cron.d为每个任务创建一个文件,让脚本在执行完毕后删除自己的文件。

选项B(每天检查是否应发送提醒):

我知道这不是你问的问题,但我也建议把它作为一项日常工作处理起来可能更干净。您可以安排这样的每日cron作业:

0 22 * * * /home/emailbot/bin/send_reminder_emails.py

所以,在这个例子中,每天,每个月,每周的每个星期的0,22小时(晚上10点),检查我们是否应该发送提醒电子邮件。

在send_reminder_emails.py脚本中,您可以检查需要“今天”发送的任何提醒的记录(可能是JSON或YML文件,或您的数据库,或自定义格式)。如果没有,则脚本退出,如果有,则向列表中的每个人发送提醒。 (可选)您可以在提醒过期时定期清理文件中的条目,也可以定期清理。

然后,您需要做的就是每次生成发票时都在提醒文件中添加一个条目。

with open("reminder_list.txt", "a") as my_file:
    my_file.write("Invoice# person@email.com 2016-12-22")

此方法的另一个好处是,如果您的服务器因维护而关闭,您可以通过检查电子邮件日期是否已过datetime.datetime.now() >= datetime(2016,12,22)来保留条目并立即发送。如果你这样做,你还需要保留一个真/假标志,指示电子邮件是否已经发送(这样你就不会向客户发送垃圾邮件)。