14

我有一个独立脚本,它可以抓取页面、启动与数据库的连接并将数据库写入其中。我需要它在 x 小时后定期执行。我可以使用 bash 脚本和伪代码来实现:

while true
do
  python scraper.py
  sleep 60*60*x
done

从我读到的关于消息代理的内容中,它们用于从一个正在运行的程序向另一个程序发送“信号”,原则上就像 HTTP。就像我有一段代码接受用户的电子邮件 ID 一样,它将带有电子邮件 ID 的信号发送到另一段将发送电子邮件的代码。

我需要芹菜在heroku上运行定期任务。我已经在单独的服务器上有一个 mongodb。为什么我需要为rabbitmq或redis运行另一个服务器只是为了这个?我可以在没有经纪人的情况下使用芹菜吗?

4

3 回答 3

16

Celery 架构旨在跨多个服务器扩展和分发任务。对于像您这样的网站,这可能是一种矫枉过正。通常需要队列服务来维护任务列表并指示已完成任务的状态。

您可能想看看 Huey。Huey 是小型 Celery “克隆”,只需要 Redis 作为外部依赖项,而不是 RabbitMQ。它仍然使用 Redis 队列机制来排列队列中的任务。

还有高级 Python 调度程序,它甚至不需要 Redis,但可以在进程中保存队列的状态。

或者,如果您有非常少量的定期任务,没有延迟任务,我会使用 Cron 和纯 Python 脚本来运行任务。

于 2014-10-07T07:37:24.637 回答
3

正如 Celery文档所解释的:

Celery 通过消息进行通信,通常使用代理在客户端和工作人员之间进行调解。为了启动任务,客户端将消息添加到队列中,然后代理将消息传递给工作人员。

您可以使用现有的 MongoDB 数据库作为代理。请参阅使用 MongoDB

于 2014-10-07T07:36:30.140 回答
0

对于这样的应用程序,最好使用Django Background Tasks

从 PyPI 安装:

pip install django-background-tasks

添加到 INSTALLED_APPS:

INSTALLED_APPS = (
    # ...
    'background_task',
    # ...
)

迁移数据库:

python manage.py makemigrations background_task
python manage.py migrate

创建和注册任务

要注册任务,请使用背景装饰器:

from background_task import background
from django.contrib.auth.models import User

@background(schedule=60)
def notify_user(user_id):
    # lookup user by id and send them a message
    user = User.objects.get(pk=user_id)
    user.email_user('Here is a notification', 'You have been notified')

这会将 notify_user 转换为后台任务函数。当您从常规代码中调用它时,它实际上会创建一个 Task 对象并将其存储在数据库中。然后,数据库包含有关稍后实际需要运行的函数的序列化信息。这确实限制了调用函数时可以传递的参数——它们都必须可序列化为 JSON。因此,为什么在上面的示例中传递的是 user_id 而不是 User 对象。

正常调用 notify_user 将安排原始函数在 60 秒后运行:

notify_user(user.id)

这是默认的调度时间(在装饰器中设置),但它可以被覆盖:

notify_user(user.id, schedule=90) # 90 seconds from now
notify_user(user.id, schedule=timedelta(minutes=20)) # 20 minutes from now
notify_user(user.id, schedule=timezone.now()) # at a specific time

您也可以立即在同步模式下运行原始功能:

notify_user.now(user.id)   # launch a notify_user function and wait for it
notify_user = notify_user.now   # revert task function back to normal function. 

对测试很有用。您可以在调度任务时指定详细名称和创建者:

 notify_user(user.id, verbose_name="Notify user", creator=user)
于 2018-07-11T13:17:21.850 回答