Celery的最佳实践

Celery

简介

Celery是一个用于执行异步任务的框架。这个框架使用python编写,使用这个框架,很容易就能将Web应用中的一些耗时的作业转交给工作池,让工作池中的worker以异步的方式执行这些作业。

架构

Celery架构

一般通过启动一个或多个worker进程来部署Celery。这些worker进程连接上消息代理(以下称之为broker)来获取任务请求。broker随机将任务请求分发给worker。通过调用Celery的API,用户生成一个任务请求,并且将这个请求发布给broker。在worker完成任务后,将完成的任务信息发送给broker。
通过启动新的worker进程并让这些进程连上broker,可以很方便的扩展worker池。每个worker可以和其他的worker同步执行任务。

broker的选择

broker

官方支持的broker列表如下

Name Status Monitoring Remote Control
RabbitMQ Stable Yes Yes
Redis Stable Yes Yes
Amazon SQS Stable No No
Zookeeper Experimental No No

存储结果

如果需要存储任务的结果,需要给Celery配置一个result store。Redis和memcache是很好的选择。

创建Celery应用

首先,安装Celery

pip install celery

然后,为celery应用创建一个模块。对于小的应用,通常的做法是把所有代码放在一个叫tasks.py的文件中

import celery
app = celery.Celery('example')

编写任务

task 是Celery中最基本的单元。Celery有很多装饰器来定义task,只需要编写一个函数并且加上一个装饰器,就能注册一个能异步执行的任务,比如下面这个函数

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

通过下面的代码,就能让这个任务异步执行

add.delay(1,2)

最佳实践

管理异步任务

Celery中的任务都是异步执行的,也就是说,在主进程中调用Celery函数后,这个函数会将任务信息发送给broker后立刻返回。
有两种方式可以获取任务执行的结果。一种方式是将执行的结果持久化,比如写入数据库。
在大多数情况下,将异步任务的结果写入数据库是很低效的。另外一种方式是使用Celery中结果存储(result store)的功能。这个功能是可选的,需要配置相关的参数。

选择序列化格式

Celery任务的输入和输出都要经过序列化和反序列化。序列化会带来一系列的问题,最好在设计任务的时候就将这点考虑到。
Celery默认会使用Pickle来对消息进行序列化。Pickle的好处是简单易用,但是在使用的过程中会有一些坑。当代码发生变动时,已经序列化的对象,反序列化后依然是变更前的代码。
好的实践是使用JSON作为序列化格式,使用JSON,不仅可以强迫开发者认真地设计参数,还可以避免使用pickle带来的安全隐患。
使用下面的配置

CELERY_TASK_SERIALIZER=json

短任务

在Celery的worker池中,worker并发地执行任务。因此,将任务设置成多线程是有意义的。每个任务应该尽可能做最小的有用工作量,以便尽可能高效地分配工作。
在发布任务的过程中,我们会将任务通过网络发给broker,broker通过网络发给worker,在worker上对任务进行反序列化,这个过程中的开销比线程之间传递任务信息要大的多。我们在设计任务的时候要考虑到这一点。如果把向数据库插入一条数据作为一个Celery任务,对资源的利用率将是不高的。但是,在同一个任务中进行1次API调用而不是几次,将会有很大的区别。
短任务会让部署和重启变得容易些。并且会比那些长任务更加不容易出错。

为任务设置超时

当执行一个任务成百上千次时,由于网络问题可能导致一个任务卡住,导致队列被阻塞而不能处理更多的任务。可以通过设置hard timeouts和soft timeouts来解决这个问题。
相关文档见这里

创建幂等的任务

任务可能会由于各种各样的原因报错或者被打断。在分布式系统中,与其想办法处理所有可能导致出错的情景,不如在设计任务时实现幂等性。在任务开始时,永远不要预设系统的状态。尽可能不要改变外部的状态。
任务在重跑时带来的副作用越小,一个分布式的系统就越能够自我修复。举个例子,当一个预想不到的错误发生时,幂等的任务只要告诉Celery去重跑就行。如果错误是短暂的,任务的幂等性能使得系统在没有人为干涉的情况下能很快自我修复。

使用acks_late

当worker收到broker发来的任务时,worker会向broker回复一个确认信息(acknowledgement)。通常情况下,broker在收到这个ack后,会将这个任务从队列中移除。但是,加入worker在执行任务的时候突然挂掉,并且已经向broker发送的确认信息,这个任务将不会再次执行。Celery在检测到worker挂掉的情况下,会尝试向其他的worker重新发送这个任务信息。但是在一些极端情况下,比如网络挂掉,硬件错误或者其他的场景下,Celery不能正确地处理这一情况。
可以通过配置acks_late=True
,使worker只有在任务完成(成功/失败)的情况下,才向broker发送确认信息。在任务信息不能丢失的场景中,这个功能是及其有用的。
但是,只有在任务被设计成幂等,以及短任务的情况(broker在将任务发送给新的worker前,会保留任务信息一段时间)下,才有用的一个配置项。

自定义task类

虽然任务看起来像一个函数,但是在用Celery装饰器装饰那个函数时,实际上是返回了一个类,这个类实现了__call__方法。(这也是为什么task可以被绑定到self,并且可以使用delayapply_async方法的原因)。这个装饰器使用起来很方便,但是有些情况下,一些任务可能会有同样的特性,或者执行同样的流程,如果继续沿用函数式的写法,可能不会很好地表达这种特性。

通过创建celery.Task的抽象子类,可以通过继承,来构建一套其他任务所需的工具和行为。子类的常见行为包括,设置rate和重试行为,初始化,甚至是一些配置项。
比如下面这个例子

class SubTask(Task):
    abstract = True
    default_retry_delay = 1
    max_retries = 3
    ignore_result = True
    task_time_limit = 15
    acks_late = True

Canvas

Celery的文档中提到了一些可以将多个任务组成一个工作流的方法。现在就开始熟悉这些方法吧。不损害上面原则的前提下,它们提供了一些方法来完成复杂的任务。尤其是chord,这个方法让任务并发执行,并且在任务完成的时候将结果传递给其他的任务。当然,这样的特性要求配置一个result store。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容

  • 写在前面 本最佳实践是基于作者有限的经验,欢迎大家共同讨论,可以持续维护此最佳实践。另本文中所使用的环境为Mac&...
    geekpy阅读 21,000评论 1 13
  • celery的python实践 简介 Celery是专注实时处理和任务调度的分布式任务队列。 在程序的运行过程中,...
    jj_jump阅读 561评论 0 0
  • 许多Django应用需要执行异步任务, 以便不耽误http request的执行. 我们也可以选择许多方法来完成异...
    青峰星宇阅读 23,006评论 0 52
  • 1.定义: Celery是一个异步的任务队列(也叫做分布式任务队列) 2.工作结构 Celery分为3个部...
    四号公园_2016阅读 28,708评论 5 60
  • celery是简单的处理大量消息的分布式系统。可以用celery异步处理各种任务,并且支持水平地扩展处理能力。在运...
    Devops_cheers阅读 5,574评论 0 2