1.安装各种扩展
pip install celery[gevent]
2.demo
celeryconfig.py
CELERY_ACCEPT_CONTENT = ['json']
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_EVENT_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
# maybe usefull later
# CELERY_ANNOTATIONS = {
# 'celerytasks.add': {'rate_limit': '2/m'}
# }
#codeing=utf8
import os
import sys
from celery import Celery
sys.path.insert(0, os.path.dirname(__file__))
REDIS_URL = 'redis://:redispasswd@127.0.0.1:6379/0'
app = Celery('celerytasks', backend=REDIS_URL, broker=REDIS_URL)
app.config_from_object('celeryconfig')
@app.task(bind=True)
def add(self, x, y):
return x + y
3.运行
启动worker 消费者
celery --app=celerytasks worker --loglevel=info
启动 生产者 命令行[(pyenv27) BJ****:scelery ****$ python
Python 2.7.10 (default, Oct 23 2015, 19:19:21)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from celerytasks import add
>>> add.name
'celerytasks.add'
>>> add.delay(1, 2).get()
3
pip install django-celery
在view层代码里import task函数,并调用即可,类似命令行。只不过把调用celery task由命令行换成view 函数
更多功能:使用Django的settings做为celery配置,使用Django ORM DB等
5.与tornado集成
#coding=utf8
from tornado import gen
from tornado.web import asynchronous, RequestHandler
from celerytasks import add
class TestCeleryHandler(RequestHandler):
@asynchronous
@gen.coroutine
def get(self):
x = self.get_argument("x", 0)
y = self.get_argument("y", 0)
result = yield self._call_task(int(x), int(y))
self.write({"result": result})
self.finish()
@gen.coroutine
def _call_task(self, x, y):
result = add.delay(x, y)
raise gen.Return(result.get())
按照文档中的写法报错,task function不可序列化,所以才有了上面的丑恶代码。文档中写法
@asynchronous
@gen.coroutine
def get(self):
response = yield gen.Task(celerytask.add.apply_async, args=[3,4])
self.write(str(response.result))
self.finish()
本人不才,按文档写一直报错:
TypeError: <function wrapper at 0x10f0c1cf8> is not JSON serializable
就是yield gen.Task(celerytask.add.apply_async, args=[3,4])一句中的celerytask.add.apply_async不可序列化。大神若知,望留言指点
没有什么高深的,就是在request handler里yield调用celery task。只需要注意使用coroutine避免阻塞
当然每个task都要写一个handler来调用,确实很麻烦。tornado-celery可以帮助完成上面无聊的handler代码
pip install tornado-celery
pip install tornado-redis <如果broker是redis,必须安装>
安装完了,一行代码也不用写,
直接启动web服务
python -m tcelery --port=7777 --app=celerytasks
这行命令接受一个app参数就是你的celery task app名,启动了一个tornado web服务,提供如下URL让你访问
/apply/(.*)/ 同步提交任务
/async_apply/(.*)/ 异步提交任务
/tasks/result/(.*)/ 获取结果
/tasks/revoke/(.*)/ 取消任务
curl -X POST -d '{ "args": [ 1, 2 ] }' http://localhost:7777/apply-async/celerytasks.add/
返回{"task-id": "013864e1-a6b2-47cd-b78f-c43adc134e08", "state": "PENDING"}
curl -X GET http://localhost:7777/tasks/result/013864e1-a6b2-47cd-b78f-c43adc134e08 查看结果
少写了handler代码,代价就是不灵活,参数固定死了是args , kwargs.此外,每个http请求只能invoke一个task,不能自定义chain,group,map等等高级任务调度
6.redis 变化
当worker启动时,会连接broker,此处使用redis。可命令行查看keys,默认worker启动会创建3个set
redis-cli -a redispasswd
127.0.0.1:6379> keys *
1) "_kombu.binding.celery"
2) "_kombu.binding.celeryev"
3) "_kombu.binding.celery.pidbox"
当有任务提交后,会再创建一个队列,默认key名叫celery,生产者将消息push进来,消费者将消息pop取走。并且会以每个任务的id做为key,以该任务的状态(结果)的序列化值作为value(字符串类型),写到result backend
[127.0.0.1:6379> type celery-task-meta-9a4fa89c-2996-444c-9983-b24fec25f14e
string
127.0.0.1:6379> get celery-task-meta-9a4fa89c-2996-444c-9983-b24fec25f14e
"{\"status\": \"SUCCESS\", \"traceback\": null, \"result\": 5000, \"children\": []}"