任务调度是后端重要的组成部分,用于的场景非常广泛,比如生产系统中跑一些定时任务,涉及到上千台机器管理就很困难;Web应用中用户触发的操作比较耗时,这部分可以放到异步任务中处理;离线数据处理多个任务,并且任务间直接有依赖关系...
分布式任务调度通常有以下几类实现方式,一类是基于机器调度的方式,如CT(Contab Task),百度内部是这种方式,定时执行指定机器上的具体指令,通常需要先把任务脚本发布到具体机器,提供给机器调度(worker);第二类如Gearman/Celery,Client将任务发送给Job Server,Server根据后端负载情况,将任务投递到Worker;还有一类是编程框架层面提供的Schedule,功能相对局限,比如只能当前这台机器执行,当前机器挂了,任务也就挂了。
目前自己工作内容涉及到不少离线数据计算,这类任务是基于Python实现,公司内部现用调度系统对非Java支持不完善,而离线任务较多,其中任务间还有依赖关系,使用系统自带的Contab,不管从任务稳定性,还是任务间依赖关系的处理都不完善,最终选择部署Celery集群作为离线任务调度框架。Celery是一个专注于实时处理的任务队列,同时也支持任务调度,配套的任务监控也很完善,可以使用Flower作为可视化监控工具。
1、Celery任务架构
Celery有三个核心组件:Celery client: 用于发布后台作业,当与应用一起工作的时候,客户端与 Flask 应用一起运行。
Celery workers: 运行后台作业的进程。Celery 支持本地和远程的 workers,可以在本地服务器上启动一个单独的 worker,也可以在远程服务器上启动worker。
Broker: 消息代理,客户端通过消息队列和 workers 进行通信,Celery 支持多种方式来实现这些队列。最常用的代理就是 RabbitMQ 和 Redis。
2、编写任务实例
(1)安装celerypip install celery
(2)celery_con.pyfrom celery import Celery
import time
celery = Celery('tasks', broker='redis://localhost:6379/0')
(3)task.pyfrom celery_con import app
@app.task
def test(x, y):
time.sleep(5)
return x + y
@app.task
def scan(x,y):
time.sleep(1)
return x-y
(4)发布任务celery -A task worker -c 2
(5)触发任务from task import test,scan
res=test.delay(2,2)
print res.get()
3、任务依赖
任务间通常存在依赖关系,例如后续任务依赖前一个任务的结果,或者是依赖几个任务的完成,前几个任务有可能支持并行、也有可能串行,Celery提供了signature和chain,可以保证任务按照用户构想执行。比如:
from celery import chain
from proj.tasks import add
# (4 + 4) * 8
chain(add.s(4, 4) | add.s(8))().get()
64
上面这种示例是后续任务依赖前一步的结果,有时候不需要前一步的结果,只需要前一步任务完成(add.s(4,4) | add.si(5,6)).get
4、异常处理
@app.task(bind=True, default_retry_delay=300, max_retries=5)
def my_task_A():
try:
print("doing stuff here...")
except SomeNetworkException as e:
print("maybe do some clenup here....")
self.retry(e)
一般添加 default_retry_delay 重试等待时间和 max_retries 重试次数来限定,防止任务无限重试。
5、任务监控
作为一个完善的任务监控系统,需要有对任务的监控,Flower提供了对Celery任务的监控,使用起来也特别方便,可以通过UI对查看任务详细、终止任务、重试任务。
flower --port=5555 --broker=redis://localhost:6379/0
broker指定成Celery的broker即可
以上是Celery的简单用法,详细的可查看Celery官方文档。