不管是使用什么编程语言,使用什么框架。在服务器上执行耗时操作,比如网络请求视频转码图片处理等。如果想实现快速响应客户端的需求,则必须使用任务队列。任务队列是一个单独的程序,和网站没有直接关系,任务队列提供了接口,能在网站中通过代码操作任务队列,比如:添加任务,查看任务结果等。今天我们来说一下在Flask中使用Celery的正确姿势。

 

一、Celery介绍:

Celery 是一个简单、灵活、可靠的分布式系统,用于处理大量消息,同时提供维护此类系统所需的工具。它是一个专注于实时处理的任务队列,同时也支持任务调度。关于更详细的Celery的介绍,可以查看Celery的官方文档:https://docs.celeryproject.org/en/stable/getting-started/introduction.html

通过以下命令即可安装Celery:

pip install celery

我们项目使用Python开发,因此通过pip安装。如果读者使用Node.js,可以使用node-celery-ts,如果是用PHP,则可以使用celery-php

 

二、Broker和Backend:

要理解Broker和Backend,首先来说一下Celery的工作原理。来看下这幅图:

15e68efc7da5882073ea50a2a6308a20.png

Celery支持手动发布任务,也支持定时任务。不管任务从哪里来,会先把任务存放到Broker(中间人)中,常用作Celery Broker的有Redis、RabbitMQ、数据库等,其中Redis和RabbitMQ的稳定性和效率是最高的。接着Celery会生成worker,来从Broker中读取任务执行。执行完成后,再把执行后的结果存放到Backend中,常用作Celery Backend的有Redis和数据库。

 

Celery官方推荐的Broker和Backend搭配为:RabbitMQ(Baoker)+Redis(Backend)。我们平常一台服务器上运行Redis和RabbitMQ有点太浪费了,而且RabbitMQ只有在消息非常大的时候才能体现优势,因此一般我们直接使用Redis作为Broker和Backend即可。

 

三、Redis安装和使用:

Redis官方是只有Linux版本的,关于Redis在Linux的安装,读者可以直接看官方文档:https://redis.io/download,或者使用带有Redis的Docker即可。Windows安装Redis教程也有些地方需要注意,因此我单独写了一份文档,读者可以参考这里:在Windows上安装Redis超详细文档

 

四、Flask中使用Celery:

Celery和Redis都安装成功后,就可以在Flask中集成Celery了。当然Flask官方文档也描述了如何集成Celery,但是那种方式不适合现实中大型项目结构的,很容易引起循环引用的问题。这里我的源码结构如下:

|- project
  |-- apps
    |--- auth
       |---- views.py
  | -- app.py
  | -- mycelery.py

其中和celery有关系的文件如下:

  • mycelery.py:创建celery对象,并且添加了任务。
  • app.py:对celery进行app绑定。
  • views.py:调用celery中的任务。

就以发送邮件为例,我的这三个文件的代码如下:

mycelery.py:

from flask_mail import Message
from exts import mail
from celery import Celery

# 定义任务函数
def send_mail(recipient,subject,body):
  message = Message(subject=subject,recipients=[recipient],body=body)
  mail.send(message)
  return {"status": "SUCCESS"}


# 创建celery对象
def make_celery(app):
  celery = Celery(app.import_name, backend=app.config['CELERY_RESULT_BACKEND'],
                  broker=app.config['CELERY_BROKER_URL'])
  TaskBase = celery.Task

  class ContextTask(TaskBase):
    abstract = True

    def __call__(self, *args, **kwargs):
      with app.app_context():
        return TaskBase.__call__(self, *args, **kwargs)

  celery.Task = ContextTask
  app.celery = celery

  # 添加任务
  celery.task(name="send_mail")(send_mail)

  return celery

app.py:

from mycelery import make_celery

# 构建celery
celery = make_celery(app)

views.py:

from flask import current_app, jsonify

@bp.route("/mail")
def mail_test():
  try:
    email = request.args.get("mail")
    subject="邮箱主题"
    body = "邮箱内容"
    current_app.celery.send_task("send_mail",(email,subject,body))
    return jsonify("success")
  except Exception as e:
    print(e)
    return jsonify("fail")

 

五、运行Celery:

打开cmd终端,然后输入以下命令即可运行Celery:

celery -A app.celery worker --loglevel=info -P gevent

其中的celery -A是固定写法,app代表我的app.py模块,celery代表我的app.py中的celery对象,--loglevel代表日志级别,如果在windows上,还需要使用-P gevent参数,并且需要通过pip安装gevent库。以上即成功运行了Celery,我们访问发送邮件的URL(/mail),即可成功使用celery异步发送邮件了。

 

六、访问任务结果:

在实际业务场景中,经常需要查看某个任务的执行状态。比如视频转码,我们会在客户端查看是否转码成功。这时候可以定义一个查看任务状态的URL,将任务状态返回。比如:

@app.route('/status/<task_id>')
def taskstatus(task_id):
    task = long_task.AsyncResult(task_id)
    if task.state == 'PENDING':
        # 任务还没开始
        response = {
            'state': task.state,
            'status': '排队中...'
        }
    elif task.state != 'FAILURE':
        response = {
            'state': task.state,
            'status': task.info.get('status', '')
        }
        if 'result' in task.info:
            response['result'] = task.info['result']
    else:
        # 如果不是PENDING,或者SUCCESS,那么可能是出现异常了
        response = {
            'state': task.state,
            'status': str(task.info),  # 返回错误信息
        }
    return jsonify(response)

以上便是Flask中使用Celery的全部内容,可以说比较详细的,都是干货输出,希望对你有帮助。