Django使用Celery实现异步任务

什么是Celery?

  • Celery是一个简单、灵活且可靠的,处理大量消息的分布式系统
  • 专注于实时处理的异步任务队列
  • 同时也支持任务调度

如图所示,整个Celery架构由4部分组成:user、broker、workers、task result。其中:

user负责生成需要处理的任务,然后交给broker任务队列,等待被处理,workers可以由多个worker组成,然后从broker中获取需要出处理的任务进行处理,处理完成后,将处理结果放入task result中。

换一种说法:A(user)在不停的做汉堡包,每做好一个汉堡包就将其放在桌子上(broker),桌子上坐着3个人(workers),每个人都从桌子上拿汉堡包吃,没吃完一个就在本子上做上记录(task result)。

大概说明了一下Celery,然后说说在Django中的应用

这里假设一个场景:有一个博客网站,每当有用户访问博客网站的文章时,其文章的被浏览次数就进行加1操作。

对于这种情况,最简单的方法就是在后端实时处理,就是当有用户访问的时候,在后端的业务逻辑中直接对文章的被浏览次数做加1操作,然后将文章返回给前端呈现在用户的面前。但是这种方式有一个较大的问题,就是每次访问都会进行一次加1操作,而对数据库进行写入操作的成本是远高于读取操作的,因此很有可能会影响页面的响应速度。

基于这种情况,我们就可以使用Celery来实现异步处理,将文章被浏览次数的加1操作使用异步的方式来处理,这样就会大大的降低页面的响应时间。

下面就基于以上场景来实现在Django中使用Celery实现异步任务处理。

首先在Django项目的settings.py的同级目录下创建一个celery_config.py的文件(注意我的项目名称是my_project,根据项目名称做相应的更改):

import os

from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'my_project.settings')  # 设置django环境

app = Celery('my_project')

app.config_from_object('django.conf:settings', namespace='CELERY')  # 使用CELERY_ 作为前缀,在settings中写配置

app.autodiscover_tasks()  # 发现任务文件每个app下的tasks.py文件

然后在Django项目的settings.py的同级目录下的__init__.py文件中添加如下内容(我的项目名称是my_project):

from my_project.celery_config import app as celery_app

__all__ = ['celery_app']

接下来,我们就可以在settings.py文件中配置消息队列broker和结果存储task result了。我们这里使用的是redis:

CELERY_BROKER_URL = 'redis://:123456@localhost:6379/1'  # Broker配置,使用Redis作为消息中间件

CELERY_RESULT_BACKEND = 'redis://:123456@localhost:6379/2'  # BACKEND配置,这里使用redis

这时,我们已经配置好了消息队列中间件以及结果存储了,接下来还有异步任务生成以及消费者worker了,这里我们可以先在项目中启动worker,然后等待异步任务的产生:

celery -A my_project worker -l info

这时worker已经启动了,接下来我们需要去添加异步任务,基于以上的场景,需要在访问一个博客详情的接口时,对该博客的被浏览次数进行统计(加1操作)。博客详情接口如下:

class ArticleDetailApi(APIView):
    """获取博客文章详情"""
    def get(self, request, article_id):
        is_find = False
        detail = None
        status = 404
        article = Articles.get_one(article_id)
        if article:
            is_find = True
            status = 200
            detail_serializer = ArticleDetailSerializers((article, ), many=True)
            detail = detail_serializer.data
        data = {
            'is_find': is_find,
            'detail': detail,
        }
        return Response(data, status=status)

这时还没有加入异步任务,现在我们需要在该接口所在的app下面创建一个tasks.py文件用来存放异步任务,然后添加异步任务:

from django.db.models import F

from blog.models import Articles

from celery import shared_task


@shared_task
def browse_times_task(article_id):
    """博客浏览次数异步处理(浏览次数加1操作)"""
    Articles.objects.filter(id=article_id).update(browse_times=F('browse_times') + 1)

这时异步任务已经定义好了,接下来需要在接口函数中引入该异步任务:

class ArticleDetailApi(APIView):
    """获取博客文章详情"""
    def get(self, request, article_id):
        is_find = False
        detail = None
        status = 404
        article = Articles.get_one(article_id)
        if article:
            is_find = True
            status = 200
            detail_serializer = ArticleDetailSerializers((article, ), many=True)
            detail = detail_serializer.data
            browse_times_task.delay(article_id)  # 添加异步任务
        data = {
            'is_find': is_find,
            'detail': detail,
        }
        return Response(data, status=status)

现在我们尝试来访问该接口

请求了几次发现被浏览次数并没有发生改变,然后查看后台才发现worker报错了:

然后我在网上查了后发现这是worker在win10下运行会发生的错误,需要在启动worker是增加一个参数:

celery -A my_project worker -l info -P eventlet

这样就可以了,这时我们再尝试访问博客详情接口,发现连续请求该接口后,被浏览次数这个参数值是变化的,后台也没有报错,这样就成功了:

发布了49 篇原创文章 · 获赞 10 · 访问量 9237

猜你喜欢

转载自blog.csdn.net/heibuliuqiu_gk/article/details/103302164