Flask中Celery任务的使用
Celery是一个分布式任务队列的库,主要优势在于能够处理耗时的、计算密集型或者需要异步执行的任务
什么是任务队列,和消息队列又什么区别?
先说消息队列,最简化的模型如下代码所示,它保存到队列里面的是“消息”,消息是纯数据,比如日志
而任务队列,保存到队列中的是“任务”,任务包含了数据和怎么处理这个数据的逻辑。
# 消息队列
import queue # 创建一个先入先出的队列 message_queue = queue.Queue() def producer(): for i in range(5): message = f"Message {i}" print(f"Producing {message}") message_queue.put(message) def consumer(): while not message_queue.empty(): message = message_queue.get() print(f"Consuming {message}") if __name__ == "__main__": producer() consumer()
任务队列:
当你使用Celery调用一个函数(将其作为任务),这个函数的逻辑会被序列化并发送到消息队列中。消费这个任务的工作进程会根据队列中的任务信息来执行相应的函数。
术语
任务生产者:调用Celery提供的API、函数、装饰器产生任务并交给任务队列的都是任务生产者。
执行单元worker:属于任务队列的消费者,持续地监控任务队列,当队列中有新的任务时,便取出来执行。
任务结果存储backend:用来存储worker执行任务的结果,Celery支持不同的方式存储任务的结果,包括AMQP、Redis、memcached、MongoDB、SQLAlchemy等
任务调度器Beat:Beat进程会读取配置文件的内容,周期性地将配置中到期需要执行的任务发送给任务队列。
最简单的示例
from celery import Celery
from flask import Flask
def make_celery(app):
celery = Celery(app.import_name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
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
return celery
app = Flask(__name__)
app.config.update(
CELERY_BROKER_URL='redis://localhost:6379',
CELERY_RESULT_BACKEND='redis://localhost:6379'
)
celery = make_celery(app)
@celery.task()
def add_together(a, b):
return a + b
实际应用中的示例
app/init.py
from celery import Celery
celery = Celery('app.factory')
app/utils/celery_util.py
def init_celery(app, celery):
celery.conf.update(app.config)
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/factory.py
from flask import Flask
form app import celery
from app.utils.celery_util import init_celery
def create_app(config=None, environment=None):
app = Flask(__name__)
app.config.update({'CELERY_BROKER_URL':'redis://localhost:6379'})
init_celery(app, celery)
...
return app
app/tasks/add.py
from app import celery
@celery.task()
def add_together(a, b):
return a + b
views/add.py
from app.tasks.add import add_together
add_bp = Blueprint('add', __name__, url_prefix='/add')
@extension_bp.route('/<x>/<y>', methods=('GET',))
def index():
add_together.delay(x,y)
return 'processing'
from app import celery
from app.factory import create_app
from app.utls.celery_util import init_celery
app = create_app()
init_celery(app, celery)
bash
> celery worker -A runcelery.celery --loglevel=debug
参考:http://slides.skien.cc/flask-hacks-and-best-practices/#14
Celery最佳实践:https://denibertovic.com/posts/celery-best-practices/