celery 实例
Celery 官网 示例
官网示例:http://docs.celeryproject.org/en/master/getting-started/first-steps-with-celery.html#first-steps
一个简单例子
第一步
编写简单的纯python函数
def say(x,y):
return x+y
if __name__ == '__main__':
say('Hello','World')
第二步
如果这个函数不是简单的输出两个字符串相加,而是需要查询数据库或者进行复杂的处理。这种处理需要耗费大量的时间,还是这种方式执行会是多么糟糕的事情。为了演示这种现象,可以使用sleep函数来模拟高耗时任务。
import time
def say(x,y):
time.sleep(5)
return x+y
if __name__ == '__main__':
say('Hello','World')
第三步
这时候我们可能会思考怎么使用多进程或者多线程去实现这种任务。对于多进程与多线程的不足这里不做讨论。现在我们可以想想celery到底能不能解决这种问题。
import time
from celery import Celery
app = Celery('sample',broker='amqp://guest@localhost//')
@app.task
def say(x,y):
time.sleep(5)
return x+y
if __name__ == '__main__':
say('Hello','World')
现在来解释一下新加入的几行代码,首先说明一下加入的新代码完全不需要改变原来的代码。导入celery模块就不用解释了,声明一个celery实例app的参数需要解释一下。
- 第一个参数是这个python文件的名字,注意到已经把.py去掉了。
- 第二个参数是用到的rabbitmq队列。可以看到其使用的方式非常简单,因为它是默认的消息队列端口号都不需要指明。
第四步
现在我们已经使用了celery框架了,我们需要让它找几个工人帮我们干活。好现在就让他们干活。
celery -A sample worker --loglevel=info
这条命令有些长,我来解释一下吧。
- -A 代表的是Application的首字母,我们的应用就是在 sample 里面 定义的。
- worker 就是我们的工人了,他们会努力完成我们的工作的。
- -loglevel=info 指明了我们的工作后台执行情况,虽然工人们已经向你保证过一定努力完成任务。但是谨慎的你还是希望看看工作进展情况。
回车后你可以看到类似下面这样一个输出,如果是没有红色的输出那么你应该是没有遇到什么错误的。
第五步
现在我们的任务已经被加载到了内存中,我们不能再像之前那样执行python sample.py来运行程序了。我们可以通过终端进入python然后通过下面的方式加载任务。输入python语句。
from sample import say
say.delay('hello','world')
我们的函数会立即返回,不需要等待。就那么简单celery解决了我们的问题。可以发现我们的say函数不是直接调用了,它被celery 的 task 装饰器 修饰过了。所以多了一些属性。目前我们只需要知道使用delay就行了。
简单案例
确保你之前的RabbitMQ已经启动。还是官网的那个例子,在任意目录新建一个tasks.py的文件,内容如下:
from celery import Celery
app = Celery('tasks', broker='amqp://guest@localhost//')
@app.task
def add(x, y):
return x + y
使用redis作为消息队列
app = Celery('task', broker='redis://localhost:6379/4')
app.conf.update(
CELERY_TASK_SERIALIZER='json',
CELERY_ACCEPT_CONTENT=['json'], # Ignore other content
CELERY_RESULT_SERIALIZER='json',
CELERYD_CONCURRENCY = 8
)
@app.task
def add(x, y):
return x + y
在同级目录执行:
$ celery -A tasks.app worker --loglevel=info
该命令的意思是启动一个worker ( tasks文件中的app实例,默认实例名为app,-A 参数后也可直接加文件名,不需要 .app),把tasks中的任务(add(x,y))把任务放到队列中。保持窗口打开,新开一个窗口进入交互模式,python或者ipython:
>>> from tasks import add
>>> add.delay(4, 4)
但此时有一个问题,你突然想知道这个任务的执行结果和状态,到底完了没有。因此就需要设置backend了
修改之前的tasks.py中的代码为:
# coding:utf-8
import subprocess
from time import sleep
from celery import Celery
backend = 'db+mysql://root:@192.168.0.102/celery'
broker = 'amqp://guest@192.168.0.102:5672'
app = Celery('tasks', backend=backend, broker=broker)
@app.task
def add(x, y):
sleep(10)
return x + y
@app.task
def hostname():
return subprocess.check_output(['hostname'])
除了添加backend之外,上面还添加了一个who的方法用来测试多服务器操作。修改完成之后,还按之前的方式启动。
同样进入python的交互模型:
>>> from tasks import add, hostname
>>> r = add.delay(4, 4)
>>> r.ready() # 10s内执行,会输出False,因为add中sleep了10s
>>>
>>> r = hostname.delay()
>>> r.result # 输出你的hostname
测试多服务器
做完上面的测试之后,产生了一个疑惑,Celery叫做分布式任务管理,那它的分布式体现在哪?它的任务都是怎么执行的?在哪个机器上执行的?在当前服务器上的celery服务不关闭的情况下,按照同样的方式在另外一台服务器上安装Celery,并启动:
$ celery -A tasks worker --loglevel=info
发现前一个服务器的Celery服务中输出你刚启动的服务器的hostname,前提是那台服务器连上了你的rabbitmq。然后再进入python交互模式:
>>> from tasks import hostname
>>>
>>> for i in range(10):
... r = hostname.delay()
... print r.result # 输出你的hostname
>>>
Celery的使用技巧(Celery配置文件和发送任务)
在实际的项目中我们需要明确前后台的分界线,因此我们的celery编写的时候就应该是分成前后台两个部分编写。在celery简单入门中的总结部分我们也提出了另外一个问题,就是需要分离celery的配置文件。
第一步
编写后台任务tasks.py脚本文件。在这个文件中我们不需要再声明celery的实例,我们只需要导入其task装饰器来注册我们的任务即可。后台处理业务逻辑完全独立于前台,这里只是简单的hello world程序需要多少个参数只需要告诉前台就可以了,在实际项目中可能你需要的是后台执行发送一封邮件的任务或者进行复杂的数据库查询任务等。
import time
from celery.task import task
@task
def say(x,y):
time.sleep(5)
return x+y
第二步
有了那么完美的后台,我们的前台编写肯定也轻松不少。到底简单到什么地步呢,来看看前台的代码吧!为了形象的表明其职能,我们将其命名为client.py脚本文件。
from celery import Celery
app = Celery()
app.config_from_object('celeryconfig')
app.send_task("tasks.say",['hello','world'])
可以看到只需要简单的几步:1. 声明一个celery实例。2. 加载配置文件。3. 发送任务。
第三步
继续完成celery的配置。官方的介绍使用celeryconfig.py作为配置文件名,这样可以防止与你现在的应用的配置同名。
CELERY_IMPORTS = ('tasks')
CELERY_IGNORE_RESULT = False
BROKER_HOST = '127.0.0.1'
BROKER_PORT = 5672
BROKER_URL = 'amqp://'
CELERY_RESULT_BACKEND = 'amqp'
可以看到我们指定了CELERY_RESULT_BACKEND为amqp默认的队列!这样我们就可以查看处理后的运行状态了,后面将会介绍处理结果的查看。
第四步
启动celery后台服务,这里是测试与学习celery的教程。在实际生产环境中,如果是通过这种方式启动的后台进程是不行的。所谓后台进程通常是需要作为守护进程运行在后台的,在python的世界里总是有一些工具能够满足你的需要。这里可以使用supervisor作为进程管理工具。在后面的文章中将会介绍如何使用supervisor工具。
celery worker -l info --beat
注意现在运行worker的方式也与前面介绍的不一样了,下面简单介绍各个参数。
-l info 与--loglevel=info的作用是一样的。
--beat 周期性的运行。即设置 心跳。
第五步
前台的运行就比较简单了,与平时运行的python脚本一样。python client.py。
现在前台的任务是运行了,可是任务是被写死了。我们的任务大多数时候是动态的,为演示动态工作的情况我们可以使用终端发送任务。
>>> from celery import Celery
>>> app = Celery()
>>> app.config_from_object('celeryconfig')
在python终端导入celery模块声明实例然后加载配置文件,完成了这些步骤后就可以动态的发送任务并且查看任务状态了。注意在配置文件celeryconfig.py中我们已经开启了处理的结果回应模式了CELERY_IGNORE_RESULT = False并且在回应方式配置中我们设置了CELERY_RESULT_BACKEND = 'amqp'这样我们就可以查看到处理的状态了。
>>> x = app.send_task('task.say',['hello', 'lady'])
>>> x.ready()
False
>>> x.status
'PENDING'
>>> x.ready()
TRUE
>>> x.status
u'SUCCESS'
可以看到任务发送给celery后马上查看任务状态会处于PENDING状态。稍等片刻就可以查看到SUCCESS状态了。这种效果真棒不是吗?在图像处理中或者其他的一些搞耗时的任务中,我们只需要把任务发送给后台就不用去管它了。当我们需要结果的时候只需要查看一些是否成功完成了,如果返回成功我们就可以去后台数据库去找处理后生成的数据了。
celery使用mangodb保存数据
第一步
安装好mongodb了!就可以使用它了,首先让我们修改celeryconfig.py文件,使celery知道我们有一个新成员要加入我们的项目,它就是mongodb配置的方式如下。
ELERY_IMPORTS = ('tasks')
CELERY_IGNORE_RESULT = False
BROKER_HOST = '127.0.0.1'
BROKER_PORT = 5672
BROKER_URL = 'amqp://'
#CELERY_RESULT_BACKEND = 'amqp'
CELERY_RESULT_BACKEND = 'mongodb'
CELERY_RESULT_BACKEND_SETTINGS = {
"host":"127.0.0.1",
"port":27017,
"database":"jobs",
"taskmeta_collection":"stock_taskmeta_collection",
}
把#CELERY_RESULT_BACKEND = 'amp'注释掉了,但是没有删除目的是对比前后的改变。为了使用mongodb我们有简单了配置一下主机端口以及数据库名字等。显然你可以按照你喜欢的名字来配置它。
第二步
启动 mongodb 数据库:mongod。修改客户端client.py让他能够动态的传人我们的数据,非常简单代码如下。
import sys
from celery import Celery
app = Celery()
app.config_from_object('celeryconfig')
app.send_task("tasks.say",[sys.argv[1],sys.argv[2]])
任务tasks.py不需要修改!
import time
from celery.task import task
@task
def say(x,y):
time.sleep(5)
return x+y
第三步
测试代码,先启动celery任务。
celery worker -l info --beat
再来启动我们的客户端,注意这次启动的时候需要给两个参数啦!
mongo
python client.py welcome landpack
等上5秒钟,我们的后台处理完成后我们就可以去查看数据库了。
第四步
查看mongodb,需要启动一个mongodb客户端,启动非常简单直接输入 mongo 。然后是输入一些简单的mongo查询语句。
最后查到的数据结果可能是你不想看到的,因为mongo已经进行了处理。想了解更多可以查看官方的文档。
浙公网安备 33010602011771号