【问题标题】:Execute a code at some point of time在某个时间点执行代码
【发布时间】:2016-12-24 20:59:44
【问题描述】:

我正在创建一个由 py​​thon 烧瓶制成的网络应用程序。我想要的是,如果我创建了一张发票,它会在续订日期自动发送一封电子邮件,通知发票已续订等等。请注意更新每 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 张发票,那么服务器可能会压力过大

【问题讨论】:

    标签: python flask


    【解决方案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

    【讨论】:

    • 我其实是在寻找芹菜以外的东西
    • Crontab 将是一个不错的选择。而且很容易设置。
    • 关于 Crontab 的一些资源很容易找到,我为你挑选一些。
    • 这个用python容易实现吗?
    • 实际上,这与 Python 无关,而是与您的操作系统有关,您可以使用 python 设置 crontab 设置,但通常,您只需像 chris 所说的那样编辑 /etc/cron.d 然后一切完成了。
    【解决方案2】:

    如果我理解正确,您需要获取生成发票的日期,然后加上 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) 发送它们。如果您这样做,您还需要保留一个真/假标志,指示电子邮件是否已发送(这样您就不会向客户发送垃圾邮件)。

    【讨论】:

    • 那个cron方法会比celery好吗?
    • 很遗憾,我对芹菜没有经验,所以我不能说它是好是坏。
    • 您也可以考虑将此作为日常任务。我已经用“选项 B”更新了我的答案。
    【解决方案3】:

    “自己写。”不,真的。

    保留要发送的发票的数据库表。每张发票都有一个状态(pendingsentpaid、...等值)和一个发票日期(可能是将来)。使用cron 或类似方法定期运行(例如 1/小时、1/天)一个程序,该程序查询表中的未决发票,发票日期/时间已到达/已过,但尚未发送发票。此开票流程发送发票、更新状态并完成。此发票实用程序不会集成到您的 Flask Web 应用程序中,但作为支持程序存在于它旁边。

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

    现在,我完全理解“自己编写”的建议与典型的“购买而不是构建”原则背道而驰。但是我已经尝试了所有主要的替代方案,例如cronCelery;我对产生收入的网络应用程序的经验表明,它们不是处理数百个长期发票事件的方法。

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

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

    现在最后一个陈述并不完全正确。它描述了cron 及其一些替代品。但即使是传统的 Unix 也提供了 at 作为 cron 的副车,并且一些 cron 后续(例如 launchdsystemd)将重复和未来的事件调度捆绑在一起(以及其他厨房用具和众所周知的水槽)。即便如此,还是有一些问题:

    1. 您依赖外部调度系统。这意味着如果出现问题,可以使用另一个界面来学习、监控和调试。这些系统级调度程序和您的 Python 应用程序之间存在显着的阻抗不匹配。即使您将“在时间 X 运行事件”外包出去,您仍然需要编写 Python 代码来发送发票并将其正确地移动到您的业务工作流程中。
    2. 这些系统对于少数事件来说很漂亮,但通常缺少能够直接查看、修改、监视或调试数百个未完成事件的接口。在大量预定事件中调试生产应用程序错误是令人痛心的。您正在谈论向此外部系统提交 300 多个待处理事件。您还必须考虑如何监控和调试该使用。
    3. 这些调度程序是为“常规”而非“高价值”或“高可用性”操作而设计的。只是一个问题,如果安排了一个活动,但你需要停机(计划内或计划外)怎么办?如果在系统备份之前事件时间过去了,那会怎样?大多数cron-like 调度程序缺乏稳健处理“错过”事件或“尽早弥补”的规定。用技术术语来说,这可能是“无赖,伙计”。假设该事件触发了收款 - 或者在您的情况下是发票发行。您有数百张发票,并且开具这些发票可能对业务至关重要。系统级调度程序与您的运营需求之间的能力差距可能真的很痛苦,尤其是在您进行扩展时。

    好的,将这些事件驱动到像 Celery 这样的外部事件调度程序中怎么样?这是一个非常更好的主意。 Celery 旨在运行大量应用程序事件。它支持各种后端引擎(例如 RabbitMQ),在实践中证明可以处理数以千计的数以千计的事件,并且它具有帮助处理大量事件的用户界面选项。到目前为止,一切都很好!但是:

    1. 您会发现自己在处理安装、配置和操作外部中间件(例如 RabbitMQ)的复杂性。这项工作产生了非常高的可实现规模,但启动和运营成本是真实的。即使您将大部分数据集中到 Heroku 之类的云服务上也是如此。
    2. 更重要的是,虽然 Celery 作为近期事件的作业调度器非常出色,但作为长时间等待的调度器并不理想。在生产中,我看到了“长距离”事件的严重问题(那些发布一个月,或者在你的情况下三个月,在未来)。虽然问题并不相同,就像cron 等一样,但 Celery 长抛出事件与正常的应用程序更新和重启周期相交并不优雅。这取决于环境,但发生在 Heroku 等流行的云服务上。

    Celery 问题并非完全无法解决或致命,但长时间延迟的事件不会享受同样的“哇!Celery 让一切工作变得更好!”为大量近期事件而获得的魔力。而且你必须成为一个有点像 Celery、RabbitMQ 等的工程师和管理员。仅仅安排几百张发票,这是一个高昂的价格和大量工作。

    总而言之:虽然未来的发票计划似乎需要外包,但在实践中,将该功能保留在您的主应用程序代码中(不是直接在您的 Flask Web 应用程序中,而是作为关联的实用程序),然后将“提醒我每天运行 N 次”低级 tickler 转移到系统级作业调度程序。

    【讨论】:

      猜你喜欢
      • 2020-01-26
      • 1970-01-01
      • 1970-01-01
      • 2013-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-05
      • 1970-01-01
      相关资源
      最近更新 更多