Celery 图解

使用场景:

  1. 异步任务:一些比较耗时的操作可以交给Celery去执行,而不用等着程序处理完才知道结果,比如视频转码,邮件发送,消息推送等。
  2. 定时任务:比如定时推送消息,定时爬取数据,定时统计数据等。

原理

Celery是基于Python开发的已给分布式任务队列框架,支持使用任务队列的方式在分布机器/进程/线程任务调度。它是Python写的库,但是它实现的通讯协议也可以使用 rubyphpjavascript等调用。异步任务除了消息队列的后台执行的方式,还有一种是定时计划任务。
Celery是已给强大的分布式任务队列,它可以让任务的执行完全脱离主程序,甚至可以被分配到其他主机上运行。我们通常使用它来实现异步任务(async task)和定时任务(crontab)。它的架构组成如下图:

Celery 图解_第1张图片

462684-20181028131702577-643068583.png

图示说明:

  1. 任务(tasks):用户定义的函数,用于实现用户的功能,比如执行一个耗时很长的任务。
  2. 中间介(Broker):用于存放tasks的地方,但是这个中间介需要解决一个问题,就是可能需要存放很多tasks,而且要保证Worker能够从这里拿取任务。
  3. 执行者(Worker):用于执行tasks,也就是真正调用我们在tasks中定义的函数。
  4. 储存(backend):把执行tasks返回的结果进行存储,以供用户查看或者调用

功能模块说明:

Celery中,以上组件具体功能如下:

任务模块 Task

包含异步任务和定时任务。其中,异步任务通常在业务逻辑中被触发并发送到任务队列中,而定时任务由Celery Beat进程周期性地将任务发往任务队列。

消息中间件 Broker

Broker即为任务调度队列,接收任务生产者发来的消息(即任务),将任务存入队列。Celery本身不提供队列服务,官方推荐使用RabbitMQredis等。

任务执行单元 Worker

Worker 是执行任务的处理单元,它实时监控消息队列,获取队列中调度的任务,并执行它。

任务结果储存 Backend

Backend用于储存任务的执行结果,以供查询。同消息中间件一样,储存也可以使用RabbitMQRedisMongodb等。

Celery-详解
Celery+Redis实现异步任务(1)
Celery+Redis实现异步任务(2)
Celery+Redis实现异步任务(3)


1、背景

由于从事区块链钱包相关开发,对于区块链链上资源需要频繁的进行检查同步,在flask项目中,对于celery这个异步任务执行工具,使用的频率算是相当的高,今天,我就来简单总结一下celery的概念和使用方法。

2、形象比喻

Celery是一个异步任务的调度工具,是Distributed Task Queue,分布式任务队列,分布式决定了可以有多个worker的存在,队列表示其是异步操作,即存在一个产生任务提出需求的工头,和一群等着被分配工作的码农。

在python中定义Celery的时候,我们要引入Broker,中文翻译过来就是"中间人"的意思,在这里Broker起到一个中间人的角色,在工头提出任务的时候,把所有的任务放到Broker里面,在Broker的另一头,一群码农等着取出一个个任务准备着手做。

这种模式注定了整个系统会是个开环系统,工头对于码农们把任务做的怎样是不知情的,所以我们要引入Backend来保存每次任务的结果。这个Backend有点像我们的Broker,也是存储信息用的,只不过这里存的是那些任务的返回结果。我们可以选择只让错误执行的任务返回结果到Backend,这样我们取回结果,便可以知道有多少任务执行失败了。

3、celery具体介绍

3.1 Broker

broker是一个消息传输的中间件,它是用来存储生产出来的各种待执行任务的。每当应用程序调用celery的异步任务的时候,会向broker传递消息,而后celery的worker将会取到消息,进行程序执行,broker可以看成是一个消息队列,其中broker的中文意思是经纪人,用来发送和接受信息。这个broker有几个方案可供选择:RabbitMQ(消息队列),Redis(缓存数据库),数据库(不推荐),等等。

3.2 Backend

通常程序发送的消息,发完就完了,可能都不知道对方什么时候接受了,为此,celery实现了一个backend,用于存储这些消息以及celery执行的一些消息和结果,Backend是在Celery的配置中的一个配置项CELERY_RESULT_BACKEND,作用是保存结果和状态,如果你需要跟踪任务的状态,那么需要设置这一项,可以是Database backend,也可以是Cache backend。

对于brokers,官方推荐是rabbitmq和redis,至于backend,就是数据库,为了简单可以都使用redis。在我的项目中,都是使用redis。

4、使用

4.1 celery架构

Celery的架构由消息中间件(message broker),任务执行单元(worker)和任务执行结果存储(task result store)三部分组成。

  • 消息中间件

Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成,包括RabbitMQ,Redis,MongoDB等

  • 任务执行单元

Worker是celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。

  • 任务结果存储

Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP,redis,memcached,mongodb,SQLAlchemy,Django等

4.2 安装redis+celery

安装Redis,它的安装比较简单:

~$ pip install redis

然后进行配置,一般都在项目的config.py文件里配置:

CELERY_BROKER_URL = "redis://localhost:6379/0"

URL的格式为:redis://:password@hostname:port/db_number

URL Scheme后的所有字段都是可选的,并且默认为localhost的6379端口,也就是redis的默认端口,使用数据库0。

安装Celery:

~$ pip install celery

4.3 使用Celery

使用celery包含三个方面:1,定义任务函数 2,运行celery服务 3,客户应用程序的调用

创建一个文件tasks.py输入下列代码:

from celery import Celery

broker = "redis://localhost:6379/0"
backend = "redis://localhost:6379/1"

app = Celery("tasks", broker=broker, backend=backend)

@app.task
def add(x, y)
    return x + y

上述代码导入了celery,然后创建了celery实例app,实例化的过程中指定了任务名tasks(和文件名一致),传入了broker和backend。然后创建了一个任务函数add。下面启动

celery服务,在当前命令行终端运行:

~$ celery -A tasks worker

目录结构(celery -A tasks worker --loglevel=info这条命令当前工作目录必须和tasks.py所在的目录相同,即进入tasks.py所在目录执行这条命令)

调用delay函数即可启动add这个任务,这个函数的效果是发送一条消息到broker中去,这个消息包括要执行的函数,函数的参数以及其他消息,具体的可以看Celery官方文档。这个时候worker会等待broker中的消息,一旦收到消息就会立刻执行消息。

注意:如果把返回值赋值给一个变量,那么原来的应用程序也会被阻塞,需要等待异步任务返回的结果,因此,实际使用中,不需要把结果赋值。

使用配置文件

Celery的配置比较多,可以在官方配置文档:http://docs.celeryproject.org/en/latest/userguide/configuration.html 查询每个配置项的含义。

5. 内存泄漏问题

celery内存泄露分析

celery配置项如下

CELERYD_CONCURRENCY = 2      celery worker并发数
CELERYD_MAX_TASKS_PER_CHILD = 5   每个worker最大执行任务数

执行celery -A ansibleAPI.celery worker启动celery,通过ps -ef | grep celery可以看到两个celery worker进程(8226,8228)。

利用celery worker进行某个任务,当worker没有执行到最大任务时(即销毁重建),每执行一次任务占用内存必然有所增加,任务数为9,10时(celery均匀调度,并发数*最大任务数),分别有原8228 worker被销毁,重新创建9386 worker及原8226 worker被销毁,重新创建9564 worker,此时,运行第9次时,占用总内存有所下降,运行第10次时,总内存回到初如值,同样任务执行第19、20次情况类似。

celery并发计算规则
celery任务并发只与celery配置项CELERYD_CONCURRENCY 有关,与CELERYD_MAX_TASKS_PER_CHILD没有关系,即CELERYD_CONCURRENCY=2,只能并发2个worker,此时任务处理较大的文件时,执行两次可以看到两个task任务并行执行,而执行第三个任务时,开始排队,直到两个worker执行完毕。

结论
celery执行完任务不释放内存与原worker一直没有被销毁有关,因此CELERYD_MAX_TASKS_PER_CHILD可以适当配置小点,而任务并发数与CELERYD_CONCURRENCY配置项有关,每增加一个worker必然增加内存消耗,同时也影响到一个worker何时被销毁,因为celery是均匀调度任务至每个worker,因此也不宜配置过大,适当配置。

你可能感兴趣的:(Celery 图解)