使用 celery 为 Django 应用程序异步执行任务

Posted

技术标签:

【中文标题】使用 celery 为 Django 应用程序异步执行任务【英文标题】:Using celery to asynchronously perform a task for a Django app 【发布时间】:2016-09-23 11:46:55 【问题描述】:

在我维护的一个 Django 应用程序中,用户登录并相互交换消息,论坛风格。在任何给定时间点,我都会通过检查在过去 5 分钟内记录session 对象的人来显示谁在线。为此,我使用了 Django 插件 user_sessions,它允许像常规 ORM 一样操作会话。

我的代码是这样的:

class WhoseOnlineView(ListView):
    model = Session
    template_name = "whose_online.html"

    def get_queryset(self):
        unique_user_sessions = Session.objects.filter(last_activity__gte=(timezone.now()-timedelta(minutes=5))).only('user').distinct('user')
        users = [session.user for session in unique_user_sessions]
        users = [user for user in users if user is not None] #sanitizing None values
        return users

该网站的用户(和功能)已显着增长,并且根据 newrelic 的说法,在我所有的视图中,这个特定视图消耗的时间最多。

我想我应该将整个任务移到异步完成。我在我的生产服务器上启动并运行了 celery(使用 redis 作为消息代理)。

这可能是一项周期性任务,每 60 秒执行一次。但似乎要让它工作,我需要在数据库(或缓存)中保存结果,以便用户可以看到保存的结果,直到下一次处理其在线列表.

谁能给我一个说明性的例子来说明如何做到这一点?我正在努力解决的主要问题是如何在下一个定期任务开始之前保存(或缓存)然后显示给用户的结果。

【问题讨论】:

【参考方案1】:

你可以这样做:

首先,编写 celery 任务以找出已登录的用户,例如

# fooapp.tasks

@shared_task
def whoseonline(): 
    unique_user_sessions = Session.objects.filter(last_activity__gte=(timezone.now()-timedelta(minutes=5))).only('user').distinct('user')
    users = [session.user for session in unique_user_sessions        users = [user for user in users if user is not None] #sanitizing None values
    # save the users to the cache
    cache.set('online_users', users, 70)  # expiring in 70 seconds

并设置任务以运行一定的时间,例如60 秒,添加以下内容:

# settings.py

CELERYBEAT_SCHEDULE = 
    'whoseonline': 
        'task': 'fooapp.tasks.whoseonline',
        'schedule': timedelta(seconds=60),  # execute every 60 seconds
        'args': (),
    ,

到您的设置。那么,在你看来,你只需要这样做

class WhoseOnlineView(ListView):
    model = Session
    template_name = "whose_online.html"

    def get_queryset(self):
        # take the values from the cache
        return cache.get('online_users')

Here 更多关于缓存和here 更多关于 celery。

您还应该记住,缓存时间到期(示例中为 70 秒)应该大于您将用于 celery 任务的计划时间(示例中为 60 秒),以便视图始终有一些东西显示。

【讨论】:

嗯,我使用的缓存后端是django.core.cache.backends.locmem.LocMemCache。似乎一切都按预期工作,除了我无法正确设置或获取缓存。有什么方法可以测试缓存是否设置正确?我试过django debug toolbar,据此,缓存没有被调用。 你应该看看我传给你的关于缓存的链接。您可以在那里找到如何设置所有缓存机制以及如何使用它 这正是我正在使用的。确切地说,这部分:docs.djangoproject.com/en/1.9/topics/cache/… 这是一个非常严肃的设置,所以我有点难过我所缺少的。 我一直在诊断它,当我尝试 get 视图中存储的结果时,我得到了“无”。奇怪的是,tasks.py 中的get(紧跟在set 之后)工作得非常好。奇怪。 一定要两边使用同一个key,时间足够,随时可用。

以上是关于使用 celery 为 Django 应用程序异步执行任务的主要内容,如果未能解决你的问题,请参考以下文章

django 使用celery 实现异步任务

Django下使用celery 异步发送短信验证码

在django中使用celery异步任务和定时任务

django使用celery异步发送短信

Django中使用celery完成异步/耗时/后台任务

Celery异步任务队列/周期任务+ RabbitMQ + Django