Flask配置Celery异步任务
前言
在使用flask开发的时候,接口的返回需要很少的时间,所以我们需要将一些耗时的任务,放到异步后台去处理,例如:发送邮件,耗时的CPU任务等。在python web框架中celery这个库,可能是最合适的。
由于我使用flask的时间比较多,但是当我想把celery很好的与flask进行集成的时候,却发现并不是那么如意。花费了很久的时间去实践最后却是各种报错。出现了循环导入、app上下文、tasks not found等问题,尝试了种种却总是不如人意。
好在功夫不负有心人,在结合官方文档并查阅了大量资料后,终于把celery很好得集成在了flask项目中。我在这里记录一下,同时也希望对你们有所帮助。
配置
开发环境 | Windows10 |
---|---|
python | 3.8.6 |
flask | 2.0.x |
celery | 5.x |
broker | redis |
pool | eventlet |
simple模式
由于celery 5.0后推荐小写模式,与flask config大写规范有冲突,所以我们当同目录下创建一个celeryconfig.py
文件
celeryconfig.py
broker_url='redis://127.0.0.1:6379/1'
result_backend='redis://127.0.0.1:6379/2'
flask simple模式。
simple.py
from flask import Flask
from celery import Celery
import celeryconfig
app = Flask(__name__)
celery_app = Celery(app.import_name,
broker=celeryconfig.broker_url,
backend=celeryconfig.result_backend)
celery_app.config_from_object(celeryconfig)
@celery_app.task(name='simple/add2')
def add2(x, y):
return x + y
@app.route('/')
def index():
results = add2.delay(3, 5)
return str(results.wait())
if __name__ == '__main__':
app.run(debug=True)
这些就是单文件模式的代码,这其中我们添加了一个任务add2
,然后启动flask。
python simple.py
由于celery和flask是同级别的app,所以我们需要一个新的窗口启动celery,加入-P参数指定异步workereventlet
celery -A simple.celery_app worker -l info -P eventlet
当我们启动celery之后。看到最后一行的ready的时候,说明我们的celery已经启动成功了。
然后再看有下面标识说明我们的任务已经被添加成功了。
[tasks]
. simple/add2
同时我们查看一下celery的窗口:
simple模式就结束了
Factory模式
当然我们如果用flask写一个稍微复杂的东西的话,其实工厂模式我们应该用的更多。下面我们一起来看看工厂模式中的配置。
目录结构
首先我们先规划一个flask+celery的目录结构。然后创建下面的文件:
.
├── app
│ ├── __init__.py ——app主体文件
│ ├── celeryconfig.py ——celery配置文件
│ ├── config.py ——flask配置文件
│ ├── models.py ——模型文件
│ ├── tasks.py ——后台任务
│ └── views.py ——视图文件
├── data.db
├── .flaskenv ——flask环境变量
└── server.py ——运行文件
我们先创建一个注册celery的函数,主要功能是使用flask应用上下文。
def register_celery(celery, app):
class ContextTask(celery.Task):
abstract = True
def __call__(self, *args, **kwargs):
with app.app_context():
return self.run(*args, **kwargs)
celery.Task = ContextTask
然后我们创建create_app函数,将写好的注册celery函数加进去。
def create_app(**kwargs):
app = Flask(__name__)
app.config.from_pyfile('config.py')
db.init_app(app)
register_celery(celery=kwargs.get('celery'), app=app) # >> 注册celery
register_blueprints(app)
register_commands(app)
return app
上面这些都是我们在__init__
文件中创建的,下面我们来创建celery的app
打开server.py
from celery import Celery
from app import create_app, celeryconfig
def make_celery(app_name):
celery = Celery(app_name,
broker=celeryconfig.broker_url,
backend=celeryconfig.result_backend)
celery.config_from_object(celeryconfig)
return celery
my_celery = make_celery(__name__)
app = create_app(celery=my_celery)
我们把celery配置文件和flask工厂应用导入进来。然后创建make_celery函数生成celery应用。
生成celery应用后把celery传入到flask应用函数中去。这样把生成和注册分开写,解决了循环导入的问题。
接着我们创建一个tasks.py文件。
from server import my_celery
from .models import db, Message
@my_celery.task()
def add2(msg):
message = Message(details=msg)
db.session.add(message)
db.session.commit()
return "success"
从server文件中导入celery应用,然后创建任务。
然后在视图中引用任务。
from flask import Blueprint, jsonify
from .models import db, Message
from .tasks import add2
th = Blueprint('', __name__)
@th.route('/')
def index():
res = add2.delay("hello word")
return jsonify(res.wait())
@th.get('/msgs')
def msg_list():
messages = Message.query.all()
results = []
for message in messages:
results.append(message.to_json())
return jsonify(results)
celery的任务可以通过delay, 方法调用,参数在delay中直接传入。
详细介绍:
celery文档
这些 API 定义了标准的执行选项集,也就是下面这三个方法:
-
apply_async(args[, kwargs[, ...]])
发送一个任务消息。
-
delay(*args, **kwargs)
直接发送一个任务消息,但是不支持运行参数。
-
calling(
__call__
)应用一个支持调用接口(例如,add(2,2))的对象,意味着任务不会被一个 worker 执行,但是会在当前线程中执行(但是消息不会被发送)。
速查表
-
T.delay(arg, kwarg=value)
调用 apply_async 的快捷方式(.delay(_args, *_kwargs)等价于调用 .apply_async(args, kwargs))。
-
T.apply_async((arg,), {'kwarg': value})
-
T.apply_async(countdown=10)
从现在起, 十秒内执行。
-
T.apply_async(eta=now + timedelta(seconds=10))
从现在起十秒内执行,指明使用eta。
-
T.apply_async(countdown=60, expires=120)
从现在起一分钟执行,但在两分钟后过期。
-
T.apply_async(expires=now + timedelta(days=2))
两天内过期,使用datetime对象。
例子
delay()
方法就像一个很规则的函数,很方便去调用它:
task.delay(arg1, arg2, kwarg1='x', kwarg2='y')
用 apply_async()
替代你写的:
task.apply_async(args=[arg1, arg2], kwargs={'kwarg1': 'x', 'kwarg2': 'y'})
尽管运行十分方便,但是如果像设置额外的行参数,你必须用 apply_async
运行一下
运行之前我们需要先创建一个.flaskenv
文件,指定以下我们的FLASK_APP环境变量是server.py
FLASK_APP=server.py
好了之后,启动flask
flask run
启动celery
celery -A server.my_celery worker -l info -P eventlet
老规矩,看一下任务注册成功没
[tasks]
. app.tasks.add2
我们打开浏览器查看
可以看到执行成功了。再看看命令行。
任务已经成功的执行了。
就这样我们弄好了 flask+celery项目的配置,并成功执行了任务。
可能相比django+celery的配置就麻烦了许多,所以flask的学习就是要更多更多的去参考社区的资料。所以Google常备身边。
开源地址
为了方便学习交流,准备了开源地址:
https://gitee.com/wxhou/flask-celery-demo
参考文献
随风挥手 ——土木狗的IT生涯 | 群: 299524235 |