Celery的架构图
celery结构分析图
项目开发中经常碰到需要使用异步任务的场景,比如一个WEB请求中有运行时间很长的业务运算,如果不采用异步任务,会阻塞当前的web请求,影响用户体验。
celery是python的一个分布式任务调度模块,由消息中间件broker、任务执行体worker、任务执行结果backend三部分组成。
celery通过消息中间件broker实现消息服务,通常broker使用rabbitMQ,redis等,搭建异步任务系统时需要部署相关的消息服务,比如选择用redis,就需要部署redis-server。
任务执行体worker可以分布式的部署到多个节点,比如多台机器或多个docker中。
任务执行结果backend用于保存任务执行结果,通常使用redis、MongoDB等,仍然需要部署相关的服务。
借助docker我们可以将celery与消息服务集成在一起,提供完整的异步任务执行体。
flower(花)是python的一个专门用于监控celery的模块,比如可以在web页面实时显示celery worker的状态、任务的状态等,最重要的,flower还提供了REST API用于方便第三方应用直接触发任务执行、获取任务执行结果等,这也极大地扩展了celery的应用范围,比如一个Java应用也可以通过flower的REST接口使用celery的异步任务系统。
以下介绍一个实例,我们将celery+flower的组合封装成一个docker,然后通过REST接口触发异步任务执行,并查看任务执行结果。
首先看dockerfile:
FROM ubuntu:14.04
USER root
RUN apt-get update \
&& apt-get install -y --force-yes python python-pip supervisor redis-server \
&& apt-get clean \
&& pip install --upgrade pip && rm -rf /usr/bin/pip && ln -s /usr/local/bin/pip /usr/bin/pip \
&& pip --no-cache-dir install celery==4.0.2 redis==2.10.5 flower==0.9.2 requests==2.18.1 \
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
EXPOSE 5555
CMD ["/usr/bin/supervisord"]
我们在ubuntu 14.04基础镜像上安装了python、pip、redis-server,以及celery、flower等python module,因为在docker内要启动多个进程,所以安装supervisor通过supervisord.conf配置多进程启动。
supervisord.conf配置文件如下:
[supervisord]
nodaemon=true
logfile=/app/log/supervisord.log
[program:redis-server]
command=/usr/bin/redis-server
stdout_logfile=/app/log/redis.log
stderr_logfile=/app/log/redis.log
autostart=true
[program:celery]
command=bash -c "sleep 25 && celery worker -A task -Q common --loglevel=info"
stdout_logfile=/app/log/celery.log
stderr_logfile=/app/log/celery.log
autostart=true
[program:flower]
command=bash -c "sleep 50 && celery flower -A task --broker=redis://127.0.0.1:6379/6 --port=5555"
stdout_logfile=/app/log/flower.log
stderr_logfile=/app/log/flower.log
autostart=true
以上配置redis-server最先启动,等待25秒启动celery worker,等待50秒启动celery flower。celery注册了task下的任务列表,接下来我们看看task相关的代码。
首先定义一个celery.py文件,启动celery、加载异步task模块以及config配置
# -*- coding:utf-8 -*-
from __future__ import absolute_import
from celery import Celery
app = Celery('task', include=['task.tasks'])
app.config_from_object('task.config')
if __name__ == '__main__':
app.start()
config.py文件定义了celery的消息中间件redis数据库6以及异步任务执行结果保存redis数据库5
# -*- coding:utf-8 -*-
from __future__ import absolute_import
from kombu import Exchange, Queue
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/5'
BROKER_URL = 'redis://127.0.0.1:6379/6'
task.py文件定义具体待执行的异步任务,以add为例
from __future__ import absolute_import
from task.celery import app
import time
@app.task
def add(x, y):
time.sleep(60)
return x + y
将docker build并run运行起来:
docker build -t asyncs:1.1 .
docker run -p 55555:5555 -dt --name=asyncs asyncs:1.1
我们将flower的5555端口映射到了宿主机的55555端口,docker启动正常后,可以登录localhost:55555查看flower的web页面
最后我们尝试以curl直接调用flower的REST接口驱动task.py中定义的add任务执行,命令直接返回一个taskId,并显示状态是PENDING,
curl -X POST -d '{"args":[1,2]}' http://localhost:55555/api/task/async-apply/task.add
{"task-id": "e553be44-b2e5-4ae8-8517-6de98008e849", "state": "PENDING"}
flower的WEB页面显示任务已经启动,
等任务执行结束,界面将有result结果显示出来。也可以通过之前返回的taskId查询任务进展及结果:
curl http://localhost:55555/api/task/result/e553be44-b2e5-4ae8-8517-6de98008e849
{"task-id": "e553be44-b2e5-4ae8-8517-6de98008e849", "state": "SUCCESS", "result": 3}