`

分布式队列神器 Celery

阅读更多

        

Celery 是什么?

Celery 是一个由 Python 编写的简单、灵活、可靠的用来处理大量信息的分布式系统,它同时提供操作和维护分布式系统所需的工具。

Celery 专注于实时任务处理,支持任务调度。

说白了,它是一个分布式队列的管理工具,我们可以用 Celery 提供的接口快速实现并管理一个分布式的任务队列。

1.快速入门

(本文以 Celery4.0 为基础进行书写)

首先,我们要理解 Celery 本身不是任务队列,它是管理分布式任务队列的工具,或者换一种说法,它封装好了操作常见任务队列的各种操作,我们用它可以快速进行任务队列的使用与管理,当然你也可以自己看 rabbitmq 等队列的文档然后自己实现相关操作都是没有问题的。

Celery 是语言无关的,虽然它是用 Python 实现的,但他提供了其他常见语言的接口支持。只是如果你恰好使用 Python 进行开发那么使用 Celery 就自然而然了。

想让 Celery 运行起来我们要明白几个概念:

1.1 Brokers

brokers 中文意思为中间人,在这里就是指任务队列本身,Celery 扮演生产者和消费者的角色,brokers 就是生产者和消费者存放/拿取产品的地方(队列)

常见的 brokers 有 rabbitmq、redis、Zookeeper 等

1.2 Result Stores / backend

顾名思义就是结果储存的地方,队列中的任务运行完后的结果或者状态需要被任务发送者知道,那么就需要一个地方储存这些结果,就是 Result Stores 了

常见的 backend 有 redis、Memcached 甚至常用的数据都可以。

1.3 Workers

就是 Celery 中的工作者,类似与生产/消费模型中的消费者,其从队列中取出任务并执行

1.4 Tasks

就是我们想在队列中进行的任务咯,一般由用户、触发器或其他操作将任务入队,然后交由 workers 进行处理。

理解以上概念后我们就可以快速实现一个队列的操作:

这里我们用 redis 当做 celery 的 broker 和 backend。

(其他 brokers 与 backend 支持看这里)

安装 Celery 和 redis 以及 python 的 redis 支持:

  1. apt-get install redis-server
  2. pip install redis
  3. pip install celery

这里需要注意如果你的 celery 是 4.0 及以上版本请确保 python 的 redis 库版本在 2.10.4 及以上,否则会出现 redis 连接 timeout 的错误,具体参考

然后,我们需要写一个task:

  1. #tasks.py
  2. from celery importCelery
  3. app =Celery('tasks', backend='redis://localhost:6379/0', broker='redis://localhost:6379/0')#配置好celery的backend和broker
  4. @app.task #普通函数装饰为 celery task
  5. def add(x, y):
  6. return x + y

OK,到这里,broker 我们有了,backend 我们有了,task 我们也有了,现在就该运行 worker 进行工作了,在 tasks.py 所在目录下运行:

  1. celery -A tasks worker --loglevel=info

意思就是运行 tasks 这个任务集合的 worker 进行工作(当然此时broker中还没有任务,worker此时相当于待命状态)

最后一步,就是触发任务啦,最简单方式就是再写一个脚本然后调用那个被装饰成 task 的函数:

  1. #trigger.py
  2. from tasks import add
  3. result = add.delay(4,4)#不要直接 add(4, 4),这里需要用 celery 提供的接口 delay 进行调用
  4. whilenot result.ready():
  5. time.sleep(1)
  6. print'task done: {0}'.format(result.get())

运行此脚本

delay 返回的是一个 AsyncResult 对象,里面存的就是一个异步的结果,当任务完成时result.ready() 为 true,然后用 result.get() 取结果即可。

到此,一个简单的 celery 应用就完成啦。

2. 进阶用法

经过快速入门的学习后,我们已经能够使用 Celery 管理普通任务,但对于实际使用场景来说这是远远不够的,所以我们需要更深入的去了解 Celery 更多的使用方式。

首先来看之前的task:

  1. @app.task #普通函数装饰为 celery task
  2. def add(x, y):
  3. return x + y

这里的装饰器app.task实际上是将一个正常的函数修饰成了一个 celery task 对象,所以这里我们可以给修饰器加上参数来决定修饰后的 task 对象的一些属性。

首先,我们可以让被修饰的函数成为 task 对象的绑定方法,这样就相当于被修饰的函数 add 成了 task 的实例方法,可以调用 self 获取当前 task 实例的很多状态及属性。

其次,我们也可以自己复写 task 类然后让这个自定义 task 修饰函数 add ,来做一些自定义操作。

2.1 根据任务状态执行不同操作

任务执行后,根据任务状态执行不同操作需要我们复写 task 的 on_failure、on_success 等方法:

  1. # tasks.py
  2. classMyTask(Task):
  3. def on_success(self, retval, task_id, args, kwargs):
  4. print'task done: {0}'.format(retval)
  5. return super(MyTask, self).on_success(retval, task_id, args, kwargs)
  6. def on_failure(self, exc, task_id, args, kwargs, einfo):
  7. print'task fail, reason: {0}'.format(exc)
  8. return super(MyTask, self).on_failure(exc, task_id, args, kwargs, einfo)
  9. @app.task(base=MyTask)
  10. def add(x, y):
  11. return x + y

嗯, 然后继续运行 worker:

  1. celery -A tasks worker --loglevel=info

运行脚本,得到:


再修改下tasks:

  1. @app.task #普通函数装饰为 celery task
  2. def add(x, y):
  3. raiseKeyError
  4. return x + y

重新运行 worker,再运行 trigger.py:

可以看到,任务执行成功或失败后分别执行了我们自定义的 on_failure、on_success

2.2 绑定任务为实例方法

  1. # tasks.py
  2. from celery.utils.log import get_task_logger
  3. logger = get_task_logger(__name__)
  4. @app.task(bind=True)
  5. def add(self, x, y):
  6. logger.info(self.request.__dict__)
  7. return x + y

然后重新运行:


执行中的任务获取到了自己执行任务的各种信息,可以根据这些信息做很多其他操作,例如判断链式任务是否到结尾等等。

关于 celery.task.request 对象的详细数据可以看这里

2.3 任务状态回调

实际场景中得知任务状态是很常见的需求,对于 Celery 其内建任务状态有如下几种:

参数 说明
PENDING 任务等待中
STARTED 任务已开始
SUCCESS 任务执行成功
FAILURE 任务执行失败
RETRY 任务将被重试
REVOKED 任务取消

当我们有个耗时时间较长的任务进行时一般我们想得知它的实时进度,这里就需要我们自定义一个任务状态用来说明进度并手动更新状态,从而告诉回调当前任务的进度,具体实现:

  1. # tasks.py
  2. from celery importCelery
  3. import time
  4. @app.task(bind=True)
  5. def test_mes(self):
  6. for i in xrange(1,11):
  7. time.sleep(0.1)
  8. self.update_state(state="PROGRESS", meta={'p': i*10})
  9. return'finish'

然后在 trigger.py 中增加:

  1. # trigger.py
  2. from task import add,test_mes
  3. import sys
  4. def pm(body):
  5. res = body.get('result')
  6. if body.get('status')=='PROGRESS':
  7. sys.stdout.write('\r任务进度: {0}%'.format(res.get('p')))
  8. sys.stdout.flush()
  9. else:
  10. print'\r'
  11. print res
  12. r = test_mes.delay()
  13. print r.get(on_message=pm, propagate=False)

然后运行任务:

2.4 定时/周期任务

Celery 进行周期任务也很简单,只需要在配置中配置好周期任务,然后在运行一个周期任务触发器( beat )即可:

新建 Celery 配置文件 celery_config.py:

  1. # celery_config.py
  2. from datetime import timedelta
  3. from celery.schedules import crontab
  4. CELERYBEAT_SCHEDULE ={
  5. 'ptask':{
  6. 'task':'tasks.period_task',
  7. 'schedule': timedelta(seconds=5),
  8. },
  9. }
  10. CELERY_RESULT_BACKEND ='redis://localhost:6379/0'

配置中 schedule 就是间隔执行的时间,这里可以用 datetime.timedelta 或者 crontab 甚至太阳系经纬度坐标进行间隔时间配置,具体可以参考这里

如果定时任务涉及到 datetime 需要在配置中加入时区信息,否则默认是以 utc 为准。例如中国可以加上:

  1. CELERY_TIMEZONE ='Asia/Shanghai'

然后在 tasks.py 中增加要被周期执行的任务:

  1. # tasks.py
  2. app =Celery('tasks', backend='redis://localhost:6379/0', broker='redis://localhost:6379/0')
  3. app.config_from_object('celery_config')
  4. @app.task(bind=True)
  5. def period_task(self):
  6. print'period task done: {0}'.format(self.request.id)

然后重新运行 worker,接着再运行 beat:

  1. celery -A task beat

可以看到周期任务运行正常~

2.5 链式任务

有些任务可能需由几个子任务组成,此时调用各个子任务的方式就变的很重要,尽量不要以同步阻塞的方式调用子任务,而是用异步回调的方式进行链式任务的调用:

错误示范

  1. @app.task
  2. def update_page_info(url):
  3. page = fetch_page.delay(url).get()
  4. info = parse_page.delay(url, page).get()
  5. store_page_info.delay(url, info)
  6. @app.task
  7. def fetch_page(url):
  8. return myhttplib.get(url)
  9. @app.task
  10. def parse_page(url, page):
  11. return myparser.parse_document(page)
  12. @app.task
  13. def store_page_info(url, info):
  14. returnPageInfo.objects.create(url, info)

正确示范1

  1. def update_page_info(url):
  2. # fetch_page -> parse_page -> store_page
  3. chain = fetch_page.s(url)| parse_page.s()| store_page_info.s(url)
  4. chain()
  5. @app.task()
  6. def fetch_page(url):
  7. return myhttplib.get(url)
  8. @app.task()
  9. def parse_page(page):
  10. return myparser.parse_document(page)
  11. @app.task(ignore_result=True)
  12. def store_page_info(info, url):
  13. PageInfo.objects.create(url=url, info=info)

正确示范2

  1. fetch_page.apply_async((url), link=[parse_page.s(), store_page_info.s(url)])

链式任务中前一个任务的返回值默认是下一个任务的输入值之一 ( 不想让返回值做默认参数可以用 si() 或者 s(immutable=True) 的方式调用 )。

这里的 s() 是方法 celery.signature() 的快捷调用方式,signature 具体作用就是生成一个包含调用任务及其调用参数与其他信息的对象,个人感觉有点类似偏函数的概念:先不执行任务,而是把任务与任务参数存起来以供其他地方调用。

2.6 调用任务

前面讲了调用任务不能直接使用普通的调用方式,而是要用类似 add.delay(2, 2) 的方式调用,而链式任务中又用到了 apply_async 方法进行调用,实际上 delay 只是 apply_async 的快捷方式,二者作用相同,只是 apply_async 可以进行更多的任务属性设置,比如 callbacks/errbacks 正常回调与错误回调、执行超时、重试、重试时间等等,具体参数可以参考这里

2.7 关于 AsyncResult

AsyncResult 主要用来储存任务执行信息与执行结果,有点类似 tornado 中的 Future 对象,都有储存异步结果与任务执行状态的功能,对于写 js 的朋友,它有点类似 Promise 对象,当然在 Celery 4.0 中已经支持了 promise 协议,只需要配合 gevent 一起使用就可以像写 js promise 一样写回调:

  1. import gevent.monkey
  2. monkey.patch_all()
  3. import time
  4. from celery importCelery
  5. app =Celery(broker='amqp://', backend='rpc')
  6. @app.task
  7. def add(x, y):
  8. return x + y
  9. def on_result_ready(result):
  10. print('Received result for id %r: %r'%(result.id, result.result,))
  11. add.delay(2,2).then(on_result_ready)

要注意的是这种 promise 写法现在只能用在 backend 是 RPC (amqp) 或 Redis 时。 并且独立使用时需要引入 gevent 的猴子补丁,可能会影响其他代码。 官方文档给的建议是这个特性结合异步框架使用更合适,例如 tornado、 twisted 等。

delay 与 apply_async 生成的都是 AsyncResult 对象,此外我们还可以根据 task id 直接获取相关 task 的 AsyncResult: AsyncResult(task_id=xxx)

关于 AsyncResult 更详细的内容,可以参考这里

利用 Celery 进行分布式队列管理、开发将会大幅提升开发效率,关于 Celery 更详细的使用大家可以去参考详细的官方文档

分享到:
评论

相关推荐

    详解分布式任务队列Celery使用说明

    【分布式任务队列Celery详解】 Celery是一个强大的分布式任务队列系统,它设计用于处理大量消息,并且具有高可靠性和灵活性。Celery的核心功能是实现实时任务处理和任务调度,采用生产者-消费者模式运行。在这个...

    Python并行分布式框架Celery详解

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

    Python-分布式队列计算系统

    1. **消息队列**:如RabbitMQ、Celery等,它们负责存储和传递任务。任务被放入队列后,由工作节点从队列中取出并执行。队列的使用可以确保任务按照一定的顺序处理,同时在节点之间异步通信。 2. **任务分解与结果...

    taskmaster, 为处理大型任务集而设计的简单分布式队列.zip

    taskmaster, 为处理大型任务集而设计的简单分布式队列 TaskmasterTaskmaster是一个简单的分布式队列,用于处理大量的任务。我们在 Disqus 构建了这样的任务来处理频繁但不常见的任务,如"将这里数据迁移到新架构"。...

    Python_分布式任务队列开发分支.zip

    本资料“Python_分布式任务队列开发分支”主要探讨了如何利用Python和Celery来构建分布式任务队列。 首先,我们需要理解什么是任务队列。任务队列是一种设计模式,它允许我们将任务(通常是计算密集型或I/O密集型)...

    Python-thornsproject分布式异步队列系统

    总结来说,"Python-thornsproject分布式异步队列系统"是一个基于Python、Redis和Celery构建的高效解决方案,用于处理分布式环境中的异步任务。它充分利用了Python的生态和Redis的高性能,为开发者提供了强大而灵活的...

    gocelery:Go中的Celery分布式任务队列

    Go Client / Server for Celery分布式任务队列 为什么? 参与过多个将服务器从Python迁移到Go的项目之后,我意识到Go可以提高现有python Web应用程序的性能。 由于此类Web应用程序中经常使用Celery分布式任务,因此...

    RabbitMQ实战 高效部署分布式消息队列

    RabbitMQ实战 高效部署分布式消息队列 pdf全85M,无法一次性上传,压缩成两个包。 请同时下载两个文件,然后一并解压。另一个文件在我上传的资源里面找,谢谢

    Celery:分布式任务队列(开发分支)-开源

    Celery是一个简单,灵活且可靠的分布式系统,可以处理大量消息,同时为操作提供维护该系统所需的工具。 这是一个任务队列,着重于实时处理,同时还支持任务调度。 Celery有庞大的用户和贡献者社区,您应该加入IRC或...

    python celery分布式任务队列的使用详解

    ### Python Celery 分布式任务队列的使用详解 #### 一、Celery介绍与基本使用 ##### 1.1 Celery简介 Celery 是一个高级的分布式任务队列,它支持多种消息传递机制(例如 RabbitMQ 和 Redis),并且能够很好地处理...

    基于Celery和Django的分布式自动化测试系统设计.pdf

    - **Celery**: Celery是一个基于分布式消息队列的异步任务队列/作业队列,是Python开发的分布式系统,主要用来处理大量消息。它能够管理异步任务,有着良好的扩展性。 - **Django**: Django是一个开源的高级Python ...

    Python-xTAS是基于Celery的分布式文本分析套件

    Celery是一个强大的异步任务队列,它支持多种消息代理,如RabbitMQ或Redis,用于在分布式环境中协调任务执行。在Python-xTAS中,Celery帮助处理大量文本数据,确保高效且可扩展的处理能力。 **Celery框架详解** ...

    django、celery、redis部署定时任务(实现多work、多队列).pdf

    首先,Celery是一个基于消息队列的异步任务队列/作业队列,它主要用于处理大量消息的分布式系统。Celery本身不是任务队列,而是用来管理分布式任务队列的工具。它封装了操作常见任务队列的各种操作,使开发者可以...

    Python-一个采用celery和requests构建的微博分布式爬虫

    在这个项目中,我们探讨的是如何利用Python的requests库进行网络请求,以及使用celery构建一个分布式任务队列,来实现对微博的高效、稳定爬取。 首先,`requests`库是Python中用于发送HTTP请求的库,它简化了获取...

    php2celery:PHP通过redis队列和celery worker进程发送任务

    Celery是一个开源的消息队列系统,主要用于分布式任务调度和异步处理。它支持多种消息中间件,包括Redis。Celery的工作模式是:任务被发送到消息队列(在这里是Redis),然后由Celery Worker进程从队列中取出并执行...

    celery-v4.3.0中文.pdf

    标题和描述中提到的"Celery-v4.3.0中文"是一个文档名称,它代表了分布式任务队列系统Celery的中文版官方文档,版本为4.3.0。Celery是一个由Python编写的开源异步任务队列/作业队列,基于分布式消息传递。它专注于...

    Python环境下安装使用异步任务队列包Celery的基础教程

    Celery是一个强大的异步任务队列,基于分布式消息传递,适用于需要高效处理大量任务的环境。它特别适合服务器集群的管理和维护,能够处理数以百万计的任务。Celery的设计核心是实时任务处理,同时也支持任务调度。 ...

Global site tag (gtag.js) - Google Analytics