开始使用Celery调度任务如果引入调度,周期性执行或者不阻塞请求线程,很多Django应用可以得到更好的使用。 在Django应用里,有很多方式可以用来调度任务,但是使用Celery有许多优势。它有很好的支持,伸缩性好,并且能和Django一起工作。它应用广泛,有很多资源可以帮助学习和使用它。一旦学会了,对其他项目也有好处。 Celery 3.0.x 本文档适用于Celery 3.0.x。之前或之后的版本可能有差别。 Celery简介 Celery被用来稍后执行某些代码,或用调度器调度这些代码 这有什么用呢?下面有一些例子。 第一个例子,假设用户发来一个页面请求,然后等待请求完成,浏览器加载新页面。对于他们的请求,你需要运行一些代码,而这些代码的运行时间可能比用户想要等待网页的时间要长,但是你并不真得需要在响应页面请求前执行那些代码。这时,你如果使用Celery稍后执行这些耗时的代码,就立即响应页面请求。 当你需要连接远程服务器来处理请求时会经常遇到这种情况。你的应用不能控制远程服务器响应的时间,甚至远程服务器可能是关闭的。 另一种常见情形是需要周期性执行某些代码。比如,你可能每小时查询最新的天气预报并且储存数据。你可以写个任务来执行这项工作,然后设置Celery每个小时执行一次。这个任务运行并把数据存入数据库,然后Web应用就可以获得最新的天气预报。 一个任务(task)只是一个Python函数。你可以把调度一个任务理解为延时调用哪个函数。比如,你可以需要 Celery 5分钟后使用参数(1, 3, 3)调用你的函数task1,或者你可以使函数batchjob每晚0时执行。 我们将会设置Celery来使你的任务在和其余应用代码尽可能相似的环境中执行,以使它们使用同样的数据库和Django设置。有一些差异需要记住,稍后将会讲到。 当一个任务准备执行时,Celery将它放到一个队列上,这个队列上有其它的将要执行的任务。你可以有很多队列,但是这里为了简单我们假设只有一个队列。 可以这样说,将一个任务添加到队列上仅仅只是把它加入到一个待办列表。为了执行任务,一些其它的程序--任务执行单元(worker)--将监视队列上是否有任务。当它发现队列上有任务时,它会取出并执行第一个任务,然后接着监视队列等待其它任务。你可以有很多任务执行单元,可能在许多不同的服务器上,但是我们今天假设只有一个任务执行单元。 稍后我们会讲更多关于队列,任务执行单元和其他尚未提及的重要的程序,上面这些现在已经够用了,让我们做一些工作。 本地安装Cerely 安装本地Django使用的Celery很简单--只需安装django-celery:
首先,我们配置Celery在runserver上使用。对于Celery中间件(broker,稍后介绍),将使用Django database broker implementation。现在,你只需知道Celery需要一个中间件,在开发过程中可以使用Django自带的(但是在生产环境中你必须使用一些更健壮的性能更好的) 在Django settings.py文件中: 1.添加这些代码:
注意:绝对不要在生产环境中使用Django中间件。在本教程中我们使用它只是为了节省时间。在生产环境中,你可以使用RabbitMQ或者Redis。 2.在INSTALLED_APPS中添加djcelery和kombu.transport.django:
3.创建Celery数据库,如果使用South作模式迁移:
如前所述,一个任务可以仅仅只是一个Python函数。但是,Celery必须了解它。当Celery和Django一起工作时,这很容易。只需要在你的应用中添加一个tasks.py文件,把你的任务放在那个文件中,并且装饰它们。这里有一个简单的tasks.py:
将一个函数标记为任务并不影响它正常工作。你仍可以如此调用它:z = add(1, 2)并且它会和以前一样工作。将它标记为任务可以让你用其它方式调用。 调度任务 接着上面的简单例子。我们想立即运行任务,并且不想它阻塞当前线程。只需通过在任务的名字后面添加 .delay 即可实现:
这很重要,你的任务总是导入并且引用相同的包名称。比如,依赖于你的Python路径如何设置,可能这样指向它myproject.myapp.tasks.add或者myapp.tasks.add。或者从myapp.views,你可能这样导入它 .tasks.add。但是Celery无法知道这些都是同一个任务。 djcelery.setup_loader()将会使用你的应用在INSTALLED_APPS里的包名,加上 .tasks.functionname。确保当你调度你的任务,你也使用同样的名字导入它,否则可能会出现bugs。 测试 启动一个任务执行单元 正如我们提过的,一个单独的程序,任务执行单元,用来执行你的Celery任务。下面是我们如何启动一个任务执行单元满足开发需要。 首先,打开一个新终端或者新窗口。在终端里,设置相同的Django开发环境--启动你的虚拟环境或者把它们添加到你的Python路径里,这两种方法都可以使你使用runserver运行你的项目。 现在,你可以在那个终端里这样启动一个任务执行单元:
运行任务 回到你的第一个窗口,启动一个Django终端,运行你的任务:
前面我们提到过使用Celery来避免对一个页面请求的响应延迟。下面是一个使用这种技术的简单的Django事例。 问题处理 尝试使Celery任务工作可能会很困难,因为会使用很多部分,并且这些部分之间还会相互联系。许多常见的小技巧仍然起作用:
也有一些Celery专用工具。 Eager scheduling 在你的Django设置里,可以添加:
这就是说,设置了CELERY_ALWAYS_EAGER = True后,这两个语句变得一样:
查看队列 只要你在开发时使用Django自带的中间件,你的队列就存储在Django数据库里。这样你就可以很容易的查看它。在你的应用里向admin.py添加几行:
检查结果 任何时候调度一个任务,Celery都会返回一个AsyncResult对象。你可以保存那个任务,然后稍后使用它检查任务是否执行完成,是否成功,以及结果。
另一个常见例子是周期性调度任务。Celery使用另一个程序celerybeat来实现。Celerybeat一直运行,等一个调度任务到执行时间了,celerybeat就会把它加入队列去执行。 显而易见,只有一个celerybeat程序可以运行(不像任务执行单元,只要你需要,你就可以运行任意个) 启动celerybeat和启动一个任务执行单元相似。打开另一个窗口,设置Django环境,然后:
添加这条配置信息:
默认计划任务 如果你想你的一些任务默认计划任务,不依赖于某人在安装完你的应用后在数据库里配置它们,你可以使用Django fixtures把你的计划任务作为应用的初始数据。
提示和技巧 不要把model对象传递给任务 因为任务不会立即运行,当一个任务运行并使用传入的model对象时,数据库的对应纪录可能已经改变。如果任务此时对model对象做了一些修改并且保存起来,数据库里的这些改变就会被旧数据覆盖了。 更安全的做法是保存对象,传递纪录的键名,在任务里重新获得对象。
执行一个任务的时候调度另一个完全可行。这可以用于保证第二个任务在第一个任务做完一些必要的工作之前不会运行。 不要在一个任务里等待另一个任务 如果一个任务等待另一个任务,它的任务执行单元将一直阻塞而不能做其他事情直到等待结束。这早晚造成死锁。 如果你的任务A里想要调度任务B,在任务B结束之后,做一些其他的工作,最好创建一个任务C做这些工作,在任务B结束时调度任务C。 下一步 一旦你理解了这些基础部分,最好阅读一下Celery用户手册中的部分内容。我建议从这些章节开始;其他的要不和Django没有关系,要不太过高级: 在生产环境里使用Celery 这里描述的Celery配置是为了开发方便,绝不能应用在生产环境。 为了在生产环境里使用,最重要的修改是停止使用kombu.transport.djanggo作为中间件,使用RabbitMQ或者其他健壮的可伸缩的等价物替换。 英文原文:https://www./blog/2014/06/23/scheduling-tasks-celery/ |
|
来自: River_LaLaLa > 《Python》