celery的简单使用和遇到的坑

一. celery简介

Celery 是一个基于消息传递、 处理大量消息的分布式系统, 简单、灵活且可靠。与单纯的消息队列相比较,队列中传递的消息为任务(Task/Job)信息,而不是数据类信息,
Celery 本身不是任务队列, 是管理分布式任务队列的工具. 它封装了操作常见任务队列的各种操作, 我们使用它可以快速进行任务队列的使用与管理, 专注于实时处理的任务队列,同时也支持任务调度, 其可以单机运行,也可以在多台机器上运行,甚至可以跨越数据中心运行。

特性 :

1. 方便查看定时任务的执行情况, 如 是否成功, 当前状态, 执行任务花费的时间等.
2. 使用功能齐备的管理后台或命令行添加,更新,删除任务.
3. 方便把任务和配置管理相关联.
4. 可选 prefork (多进程),  Eventlet,  Gevent, 多线程等模型并发执行.
5. 提供错误处理机制.
6. 提供多种任务原语, 方便实现任务分组,拆分,和调用链.
7. 支持多种消息代理和存储后端.
8. Celery 是语言无关的.它提供了python 等常见语言的接口支持.

组件:

Celery 扮演生产者和消费者的角色,

Celery Beat : 任务调度器. Beat 进程会读取配置文件的内容, 周期性的将配置中到期需要执行的任务发送给任务队列.
Celery Worker : 执行任务的消费者, 通常会在多台服务器运行多个消费者, 提高运行效率.
Broker : 消息代理, 队列本身. 也称为消息中间件. 接受任务生产者发送过来的任务消息, 存进队列再按序分发给任务消费方(通常是消息队列或者数据库),可以是 RabbitMQ 、 Redis, 官方推荐 RabbitMQ.
Producer : 任务生产者. 调用 Celery API , 函数, 而产生任务并交给任务队列处理的都是任务生产者.
Result Backend : 任务处理完成之后保存状态信息和结果, 以供查询, 可以是AMQP, Redis, memcached, MongoDB, SQLAlchemy. Django ORM, Apache Cassandra等.

celery各组件关系

 

二. celery使用

本次在项目中使用的worker和backend都是redis(可根据项目需要自行安装数据库依赖)
安装redis:  pip install redis==2.8.0
安装celery:  pip install celery==3.1.25

  1. 简单的异步任务

# task.py
from __future__ import absolute_import
from celery import Celery

celeryapp = Celery('celery',  broker='redis://127.0.0.1:6379/0',  backend='redis://127.0.0.1:6379/1')

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

if __name__ == '__main__':
result = add.delay(10, 20)

 

task.py中, 第一行声明使用绝对导入, 否则会报导入错误的相关异常
实例化celery对象, Celery 的第一个参数是当前模块的名称,这个参数是必须的,自定义即可, 这样的话名称可以自动生成。第二个参数是中间人关键字参数,指定你所使用的消息中间人的 地址,此处保存任务到redis 0 号库, backend参数是用来保存任务执行结果的, 保存任务到redis 1 号库, 当然也可以选择不保存结果, 库号不指定时, 默认都存在0 号库.
使用装饰器的方式定义异步任务add

任务的调用

异步任务的调用方法: 
1. add.delay(1, 2):这是apply_async方法的别名,但接受的参数较为简单;
2. add.apply_async(args=[1, 2], kwargs={'countdown':5, 'expires':60})
3. celeryapp.send_task('task.add', args=[1, 2]):可以发送未被注册的异步任务,即没有被celery.task装饰的任务;
# apply_async 可以传递更多的参数, eg: countdown=5:等待5S后再执行, eta=now+tiedelta(second=20):指定任务的开始时间, expires=60:指定任务的超时时间, retry=True: 指定任务失败后是否重试

worker的启动

celery -A task worker --loglevel=info

(

celery -A celery_server worker --loglevel=info    # 启动worker服务了,此后该worker会一直等待任务并执行, 这种方式默认是多进程启动worker
celery -A celery_server worker --loglevel=info -P solo    # 使用单进程启动
celery -A celery_server worker --loglevel=info -P eventlet     # 使用协程的方式启动。当然首先需要安装eventlet。(pip install eventlet)

)

任务的一些方法,帮助自己更灵活的使用celery

result.ready()   # 查看任务状态,返回布尔值, 任务执行完成, 返回 True, 否则返回 False.
result.get(timeout=5)   # 获取任务执行结果,可以设置等待时间
result.state   # 查看任务当前的状态, PENDING:任务等待中, STARTED:任务已开始, SUCCESS:任务执行成功, FAILURE:任务执行失败, RETRY: 任务将被重新执行, REVOKED:任务取消, PROCESS:任务执行中
result.traceback   # 如果任务抛出异常, 可以获取完整的堆栈信息

任务的执行结果

执行结果保存到redis1号库, 在1号库会有类似下面格式的键, 其对应的值就是执行结果了.
键: celery-task-meta-080ee8b0-24c4-48a0-a2dd-a1bcebb45b5b
值: {
"status": "SUCCESS",
"result": 30,
"traceback": null,
"children": [],
"task_id": 080ee8b0-24c4-48a0-a2dd-a1bcebb45b5b
}

 

2. 在项目中使用celery

如果是在项目中使用celery, 对于项目的目录结构也是有要求的.

-proj

    - __init__.py
    - celeryconfig.py
    - tasks.py
    - views.py

 

# __init__.py

from __future__ import absolute_import
from celery import Celery, platforms


celeryapp = Celery('celery_app',
broker='redis://127.0.0.1:6379/0',
backend='redis://127.0.0.1:6379/1',
)
celeryapp.config_from_object('proj.celeryconfig') # 调用 config_from_object() 来让 Celery 实例加载配置模块
celeryapp.autodiscover_tasks(['proj.tasks']) # 以列表的形式传入, 自动将异步任务注册到 Celery 实例,  也可以注册 proj 同级目录下定义的异步任务
platforms.C_FORCE_ROOT = True # 解决root用户不能启动celery的问题

 


# celeryconfig.py
CELERY_TASK_SERIALIZER = 'json' # 配置序列化任务载荷的默认的序列化方式 有: pickle,JSON,YAML 和msgpack
CELERY_RESULT_SERIALIZER = 'json' 
CELERY_ACCEPT_CONTENT = ["json"]

 


# tasks.py
from __future__ import absolute_import
import celeryapp


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

 


# views.py
from tasks import add


if __name__ == '__main__':
res = add.delay(2, 3)
print(res.status)

 

启动celery worker: celery -A proj worker --loglevel=info

 

如果需要实现多任务 多队列优先级的celery, 下面的配置可以参考

# tasks.py
@app.task
def my_taskA():
pass
@app.task
def my_taskB():
pass

# celeryconfig.py
from kombu import Exchange,Queue
# 手动定义队列
CELERY_QUEUES = (
Queue('default', Exchange('default'), routing_key='default', consumer_arguments={'x-priority': 1}), 
Queue('for_task_A', Exchange('for_task_A'), routing_key='for_task_A', consumer_arguments={'x-priority': 4}),
Queue('for_task_B', Exchange('for_task_B'), routing_key='for_task_B', consumer_arguments={'x-priority': 6})
)
# 然后定义routes用来决定不同的任务(既函数名)去哪个queue
CELERY_ROUTES = (
'my_taskA': {'queue': 'for_tesk_A', 'routing_key': 'for_tesk_A' },
'my_taskB': {'queue': 'for_task_B', 'routing_key': 'for_task_B'}
)

# 为每个task启动不同的worker
celery -A tasks worker -l info -n workerA -Q for_tesk_A 
celery -B tasks worker -l info -n workerB -Q for_task_B

 

三. 使用celery遇到的坑

 

启动celery异步任务时, 报: KeyError: 'backend'

from . import async, base
^
SyntaxError: invalid syntax

异常原因: Python3.6及其以上的版本已经将async收入关键字中, 而celery4.0版本也用到async, 如果不配置backend的话是不会有这个异常的.

解决方案: 第一种: 将celery的版本将为2.10.6,  第二种: 将celery依赖中的async更名为其他名称即可

 

启动celery成功, 调用任务时报错: [2019-03-04 19:13:17,567: ERROR/MainProcess] Received unregistered task of type 'celery_test1.test'.
The message has been ignored and discarded.

Did you remember to import the module containing this task?
Or maybe you're using relative imports?

Please see
http://docs.celeryq.org/en/latest/internals/protocol.html
for more information.

The full contents of the message body was:
b'[[], {}, {"callbacks": null, "errbacks": null, "chain": null, "chord": null}]' (77b)
Traceback (most recent call last):
File "c:\program files (x86)\python37-32\lib\site-packages\celery\worker\consumer\consumer.py", line 558, in on_task_received
strategy = strategies[type_]
KeyError: 'celery_test1.test'

解决方案: 不要将异步任务函数的定义和调用写到同一个文件中 

 

启动celery后调用任务后报错:
ERROR/MainProcess Unrecoverable error: AttributeError("'str' object has no attribute 'items'")
解决方案: 将 redis版本降低到 2.10.6 , pip install redis==2.10.6

 

[2017-08-02 19:59:04,777: ERROR/MainProcess] Task handler raised error: ValueError('not enough values to unpack (expected 3, got 0)',)
Traceback (most recent call last):
File "d:\python\python36-32\lib\site-packages\billiard\pool.py", line 358, in workloop
result = (True, prepare_result(fun(*args, **kwargs)))
File "d:\python\python36-32\lib\site-packages\celery\app\trace.py", line 525, in _fast_trace_task
tasks, accept, hostname = _loc
ValueError: not enough values to unpack (expected 3, got 0)
[2017-08-02 20:04:30,870: INFO/MainProcess] Received task: mytask.hello[ec84d3ba-98ac-44bc-be5e-09190c2712e0]
[2017-08-02 20:04:30,873: ERROR/MainProcess] Task handler raised error: ValueError('not enough values to unpack (expected 3, got 0)',)
Traceback (most recent call last):
File "d:\python\python36-32\lib\site-packages\billiard\pool.py", line 358, in workloop
result = (True, prepare_result(fun(*args, **kwargs)))
File "d:\python\python36-32\lib\site-packages\celery\app\trace.py", line 525, in _fast_trace_task
tasks, accept, hostname = _loc
ValueError: not enough values to unpack (expected 3, got 0)


解决方案: 第一种: Try to uninstall celery 4.1.0 and replace to 3.1.24 
                第二种: pip install eventlet, 启动celeryworker时: celery -A celery_app worker --loglevel=info -P eventlet

 

 

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值