前段时间需要使用rabbitmq做写缓存,一直使用pika+rabbitmq的组合,pika这个模块虽然可以很直观地操作rabbitmq,但是官方给的例子太简单,对其底层原理了解又不是很深,遇到很多坑,尤其是需要自己写连接池管理和channel池管理。虽然也有用过celery,一直也是celery+redis的组合,涉及很浅;目前打算深研一下celery+redis+rabbitmq的使用。

celery + rabbitmq初步

  • 我们先不在集成框架如flask或Django中使用,而仅仅单独使用。

简单介绍

Celery 是一个异步任务队列。一个Celery安装有三个核心组件:

  1. Celery 客户端: 用于发布后台作业。当与 Flask 一起工作的时候,客户端与 Flask 应用一起运行。

  2. Celery workers: 运行后台作业的进程。Celery 支持本地和远程的 workers,可以在 Flask 服务器上启动一个单独的 worker,也可以在远程服务器上启动worker,需要拷贝代码;

  3. 消息代理: 客户端通过消息队列和 workers 进行通信,Celery 支持多种方式来实现这些队列。最常用的代理就是 RabbitMQ 和 Redis。

安装rabbitmq和redis

  • rabbitmq安装和配置参考:http://www.cnblogs.com/cwp-bg/p/8397529.html

  • redis的安装和配置参考:http://www.cnblogs.com/cwp-bg/p/8094914.html

  • redis-py安装:

sudo pip install redis
  • redis-py操作redis参考:http://www.cnblogs.com/cwp-bg/p/8274269.html

  • 为了提高性能,官方推荐使用librabbitmq,这是一个连接rabbitmq的C++的库;

sudo pip install celery[librabbitmq]

初步使用

  • 使用redis做结果存储,使用rabbitmq做任务队列;
# tasks.py
from celery import Celeryapp = Celery('tasks', broker='amqp://username:passwd@ip:port/varhost',backend='redis://username:passwd@ip:6390/db') @app.task def add(x, y): return x + y if __name__ == '__main__': result = add.delay(30, 42)
  • broker:任务队列的中间人;

  • backend:任务执行结果的存储;

发生了什么事

  • app.task装饰后将add函数变成一个异步的任务,add.delay函数将任务序列化发送到rabbitmq;

  • 该过程创建一个名字为celery的exchange,类型为direct(直连交换机);创建一个名为celery的queue,队列和交换机使用路由键celery绑定;

  • 打开rabbitmq管理后台,可以看到有一条消息已经在celery队列中;

记住:当有多个装饰器的时候,celery.task一定要在最外层;

扩展

  • 如果使用redis作为任务队列中间人,在redis中存在两个键 celery 和 _kombu.binding.celery , _kombu.binding.celery 表示有一名为 celery 的任务队列(Celery 默认),而 celery为默认队列中的任务列表,使用list类型,可以看看添加进去的任务数据。

  • 开启worker

在项目目录下执行:

celery -A app.celery_tasks.celery worker -Q queue --loglevel=info
  • A参数指定celery对象的位置,该app.celery_tasks.celery指的是app包下面的celery_tasks.py模块的celery实例,注意一定是初始化后的实例,

  • Q参数指的是该worker接收指定的队列的任务,这是为了当多个队列有不同的任务时可以独立;如果不设会接收所有的队列的任务;

  • l参数指定worker的日志级别;

执行完毕后结果存储在redis中,查看redis中的数据,发现存在一个string类型的键值对:

celery-task-meta-064e4262-e1ba-4e87-b4a1-52dd1418188f:data

该键值对的失效时间为24小时。

分析消息

  • 这是添加到任务队列中的消息数据。
{"body": "gAJ9cQAoWAQAAAB0YXNrcQFYGAAAAHRlc3RfY2VsZXJ5LmFkZF90b2dldGhlcnECWAIAAABpZHEDWCQAAAA2NmQ1YTg2Yi0xZDM5LTRjODgtYmM5OC0yYzE4YjJjOThhMjFxBFgEAAAAYXJnc3EFSwlLKoZxBlgGAAAAa3dhcmdzcQd9cQhYBwAAAHJldHJpZXNxCUsAWAMAAABldGFxCk5YBwAAAGV4cGlyZXNxC05YAwAAAHV0Y3EMiFgJAAAAY2FsbGJhY2tzcQ1OWAgAAABlcnJiYWNrc3EOTlgJAAAAdGltZWxpbWl0cQ9OToZxEFgHAAAAdGFza3NldHERTlgFAAAAY2hvcmRxEk51Lg==",   # body是序列化后使用base64编码的信息,包括具体的任务参数,其中包括了需要执行的方法、参数和一些任务基本信息
"content-encoding": "binary", # 序列化数据的编码方式 "content-type": "application/x-python-serialize", # 任务数据的序列化方式,默认使用python内置的序列化模块pickle "headers": {}, "properties": {"reply_to": "b7580727-07e5-307b-b1d0-4b731a796652", # 结果的唯一id "correlation_id": "66d5a86b-1d39-4c88-bc98-2c18b2c98a21", # 任务的唯一id "delivery_mode": 2, "delivery_info": {"priority": 0, "exchange": "celery", "routing_key": "celery"}, # 指定交换机名称,路由键,属性 "body_encoding": "base64", # body的编码方式 "delivery_tag": "bfcfe35d-b65b-4088-bcb5-7a1bb8c9afd9"}}
  • 将序列化消息反序列化
import pickle
import base64result = base64.b64decode('gAJ9cQAoWAQAAAB0YXNrcQFYGAAAAHRlc3RfY2VsZXJ5LmFkZF90b2dldGhlcnECWAIAAABpZHEDWCQAAAA2NmQ1YTg2Yi0xZDM5LTRjODgtYmM5OC0yYzE4YjJjOThhMjFxBFgEAAAAYXJnc3EFSwlLKoZxBlgGAAAAa3dhcmdzcQd9cQhYBwAAAHJldHJpZXNxCUsAWAMAAABldGFxCk5YBwAAAGV4cGlyZXNxC05YAwAAAHV0Y3EMiFgJAAAAY2FsbGJhY2tzcQ1OWAgAAABlcnJiYWNrc3EOTlgJAAAAdGltZWxpbWl0cQ9OToZxEFgHAAAAdGFza3NldHERTlgFAAAAY2hvcmRxEk51Lg==')
print(pickle.loads(result))# 结果 { 'task': 'test_celery.add_together', # 需要执行的任务 'id': '66d5a86b-1d39-4c88-bc98-2c18b2c98a21', # 任务的唯一id 'args': (9, 42), # 任务的参数 'kwargs': {}, 'retries': 0, 'eta': None, 'expires': None, # 任务失效时间 'utc': True, 'callbacks': None, # 完成后的回调 'errbacks': None, # 任务失败后的回调 'timelimit': (None, None), # 超时时间 'taskset': None, 'chord': None }
  • 常见的数据序列化方式
binary: 二进制序列化方式;python的pickle默认的序列化方法;
json:json 支持多种语言, 可用于跨语言方案,但好像不支持自定义的类对象;
XML:类似标签语言;
msgpack:二进制的类 json 序列化方案, 但比 json 的数据结构更小, 更快;
yaml:yaml 表达能力更强, 支持的数据类型较 json 多, 但是 python 客户端的性能不如 json
  • 经过比较,为了保持跨语言的兼容性和速度,采用msgpack或json方式;

celery配置

  • celery的性能和许多因素有关,比如序列化的方式,连接rabbitmq的方式,多进程、单线程等等;

基本配置项

CELERY_DEFAULT_QUEUE:默认队列
BROKER_URL  : 代理人的网址
CELERY_RESULT_BACKEND:结果存储地址
CELERY_TASK_SERIALIZER:任务序列化方式
CELERY_RESULT_SERIALIZER:任务执行结果序列化方式 CELERY_TASK_RESULT_EXPIRES:任务过期时间 CELERY_ACCEPT_CONTENT:指定任务接受的内容序列化类型(序列化),一个列表;

采用配置文件的方式执行celery

# main.py
from celery import Celery
import celeryconfig
app = Celery(__name__, include=["task"]) # 引入配置文件 app.config_from_object(celeryconfig) if __name__ == '__main__': result = add.delay(30, 42) # task.py from main import app @app.task def add(x, y): return x + y # celeryconfig.py BROKER_URL = 'amqp://username:password@localhost:5672/yourvhost' CELERY_RESULT_BACKEND = 'redis://localhost:6379/0' CELERY_TASK_SERIALIZER = 'msgpack' CELERY_RESULT_SERIALIZER = 'msgpack' CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24 # 任务过期时间 CELERY_ACCEPT_CONTENT = ["msgpack"] # 指定任务接受的内容类型.
  • 一些方法
r.ready()     # 查看任务状态,返回布尔值,  任务执行完成, 返回 True, 否则返回 False.
r.wait()      # 等待任务完成, 返回任务执行结果,很少使用;
r.get(timeout=1)       # 获取任务执行结果,可以设置等待时间 r.result # 任务执行结果. r.state # PENDING, START, SUCCESS,任务当前的状态 r.status # PENDING, START, SUCCESS,任务当前的状态 r.successful # 任务成功返回true r.traceback # 如果任务抛出了一个异常,你也可以获取原始的回溯信息

celery的装饰方法celery.task

@celery.task()
def name(): pass
  • task()方法将任务装饰成异步,参数:

name:可以显示指定任务的名字;

serializer:指定序列化的方法;

bind:一个bool值,设置是否绑定一个task的实例,如果把绑定,task实例会作为参数传递到任务方法中,可以访问task实例的所有的属性,即前面反序列化中那些属性

@task(bind=True)  # 第一个参数是self,使用self.request访问相关的属性
def add(self, x, y): logger.info(self.request.id)

base:定义任务的基类,可以以此来定义回调函数

import celeryclass MyTask(celery.Task): # 任务失败时执行 def on_failure(self, exc, task_id, args, kwargs, einfo): print('{0!r} failed: {1!r}'.format(task_id, exc)) # 任务成功时执行 def on_success(self, retval, task_id, args, kwargs): pass # 任务重试时执行 def on_retry(self, exc, task_id, args, kwargs, einfo): pass @task(base=MyTask) def add(x, y): raise KeyError() exc:失败时的错误的类型; task_id:任务的id; args:任务函数的参数; kwargs:参数; einfo:失败时的异常详细信息; retval:任务成功执行的返回值; 
  • 另外还可以指定exchange信息等,不过一般不使用;

调用异步任务的方法

task.delay():这是apply_async方法的别名,但接受的参数较为简单;
task.apply_async(args=[arg1, arg2], kwargs={key:value, key:value}) send_task():可以发送未被注册的异步任务,即没有被celery.task装饰的任务;
# tasks.py
from celery import Celery
app = Celery()
def add(x,y): return x+y app.send_task('tasks.add',args=[3,4]) # 参数基本和apply_async函数一样 # 但是send_task在发送的时候是不会检查tasks.add函数是否存在的,即使为空也会发送成功 
  • apply_async的参数:

countdown : 设置该任务等待一段时间再执行,单位为s;

eta : 定义任务的开始时间;eta=time.time()+10;

expires : 设置任务时间,任务在过期时间后还没有执行则被丢弃;

retry : 如果任务失败后, 是否重试;使用true或false,默认为true

shadow:重新指定任务的名字str,覆盖其在日志中使用的任务名称;

retry_policy : 重试策略.

max_retries : 最大重试次数, 默认为 3 次.
interval_start : 重试等待的时间间隔秒数, 默认为 0 , 表示直接重试不等待.
interval_step : 每次重试让重试间隔增加的秒数, 可以是数字或浮点数, 默认为 0.2 interval_max : 重试间隔最大的秒数, 即 通过 interval_step 增大到多少秒之后, 就不在增加了, 可以是数字或者浮点数, 默认为 0.2 .
add.apply_async((2, 2), retry=True, retry_policy={'max_retries': 3,'interval_start': 0, 'interval_step': 0.2, 'interval_max': 0.2, })

routing_key:自定义路由键;

queue:指定发送到哪个队列;

exchange:指定发送到哪个交换机;

priority:任务队列的优先级,0-9之间;

serializer:任务序列化方法;通常不设置;

compression:压缩方案,通常有zlib, bzip2

headers:为任务添加额外的消息;

link:任务成功执行后的回调方法;是一个signature对象;可以用作关联任务;

link_error: 任务失败后的回调方法,是一个signature对象;

  • 自定义发布者,交换机,路由键, 队列, 优先级,序列方案和压缩方法:
task.apply_async((2,2), compression='zlib', serialize='json', queue='priority.high', routing_key='web.add', priority=0, exchange='web_exchange')

一份比较常用的配置文件

# 注意,celery4版本后,CELERY_BROKER_URL改为BROKER_URL
BROKER_URL = 'amqp://username:passwd@host:port/虚拟主机名'
# 指定结果的接受地址
CELERY_RESULT_BACKEND = 'redis://username:passwd@host:port/db'
# 指定任务序列化方式
CELERY_TASK_SERIALIZER = 'msgpack'
# 指定结果序列化方式
CELERY_RESULT_SERIALIZER = 'msgpack'
# 任务过期时间,celery任务执行结果的超时时间 CELERY_TASK_RESULT_EXPIRES = 60 * 20 # 指定任务接受的序列化类型. CELERY_ACCEPT_CONTENT = ["msgpack"] # 任务发送完成是否需要确认,这一项对性能有一点影响 CELERY_ACKS_LATE = True # 压缩方案选择,可以是zlib, bzip2,默认是发送没有压缩的数据 CELERY_MESSAGE_COMPRESSION = 'zlib' # 规定完成任务的时间 CELERYD_TASK_TIME_LIMIT = 5 # 在5s内完成任务,否则执行该任务的worker将被杀死,任务移交给父进程 # celery worker的并发数,默认是服务器的内核数目,也是命令行-c参数指定的数目 CELERYD_CONCURRENCY = 4 # celery worker 每次去rabbitmq预取任务的数量 CELERYD_PREFETCH_MULTIPLIER = 4 # 每个worker执行了多少任务就会死掉,默认是无限的 CELERYD_MAX_TASKS_PER_CHILD = 40 # 设置默认的队列名称,如果一个消息不符合其他的队列就会放在默认队列里面,如果什么都不设置的话,数据都会发送到默认的队列中 CELERY_DEFAULT_QUEUE = "default" # 设置详细的队列 CELERY_QUEUES = { "default": { # 这是上面指定的默认队列 "exchange": "default", "exchange_type": "direct", "routing_key": "default" }, "topicqueue": { # 这是一个topic队列 凡是topictest开头的routing key都会被放到这个队列 "routing_key": "topic.#", "exchange": "topic_exchange", "exchange_type": "topic", }, "task_eeg": { # 设置扇形交换机 "exchange": "tasks", "exchange_type": "fanout", "binding_key": "tasks", }, }# 不同task指定queueCELERY_ROUTES = {    "projq.tasks.add": { # task函数名,必须是全路径 app.tasks.method_name        "queue": "topicqueue",         "routing_key": "topic.#",    }}

-参考:

  • http://docs.celeryproject.org/en/latest/userguide/tasks.html#task-options

  • http://docs.jinkan.org/docs/flask/patterns/celery.html

  • http://www.pythondoc.com/flask-celery/first.html

  • https://blog.csdn.net/kk123a/article/details/74549117

  • https://blog.csdn.net/preyta/article/details/54288870

  • Celery消息队列----路由任务(含task指定queue)

  • 分布式任务队列 Celery 的使用(含task指定queue)

阅读原文

python之celery使用详解一相关推荐

  1. python的继承用法_【后端开发】python中继承有什么用法?python继承的用法详解

    本篇文章给大家带来的内容是关于python中继承有什么用法?python继承的用法详解,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 面向对象三大特征 1.封装:根据职责将属性和方法 ...

  2. python字典之defaultdict详解

    python字典之defaultdict详解 defaultdict为你的字典设定一个默认值或者默认值的数据类型: defaultdict接受一个工厂函数作为参数,如下来构造: dict =defau ...

  3. Python数据科学-技术详解与商业实践视频教程

    Python数据科学-技术详解与商业实践(八大案例) 网盘地址:https://pan.baidu.com/s/13QrR_5Er6LgWCWzSb7qOrQ 提取码:s7vw 备用地址(腾讯微云): ...

  4. python turtle循环图案-Python内置模块turtle绘图详解

    urtle库是Python语言中一个很流行的绘制图像的函数库,想象一个小乌龟,在一个横轴为x.纵轴为y的坐标系原点,(0,0)位置开始,它根据一组函数指令的控制,在这个平面坐标系中移动,从而在它爬行的 ...

  5. python爬虫入门实例-Python爬虫天气预报实例详解(小白入门)

    本文研究的主要是Python爬虫天气预报的相关内容,具体介绍如下. 要求是把你所在城市过去一年的历史数据爬出来. 分析网站 我们可以看到,我们需要的天气数据都是放在图表上的,在切换月份的时候,发现只有 ...

  6. python 自动化-Python API 自动化实战详解(纯代码)

    主要讲如何在公司利用Python 搞API自动化. 1.分层设计思路 dataPool :数据池层,里面有我们需要的各种数据,包括一些公共数据等 config :基础配置 tools : 工具层 co ...

  7. python序列类型-python序列类型种类详解

    python序列类型包括哪三种 python序列类型包括:列表.元组.字典 列表:有序可变序列 创建:userlist = [1,2,3,4,5,6] 修改:userlist[5] = 999 添加: ...

  8. python流程控制语句-python学习 流程控制语句详解

    ###################### 分支语句 python3.5 ################ #代码的缩进格式很重要 建议4个空格来控制 #根据逻辑值(True,Flase)判断程序的 ...

  9. python之sys模块详解_(转)python之os,sys模块详解

    python之sys模块详解 原文:http://www.cnblogs.com/cherishry/p/5725184.html sys模块功能多,我们这里介绍一些比较实用的功能,相信你会喜欢的,和 ...

最新文章

  1. R语言ggplot2可视化分面图使用facet_wrap函数和facet_grid函数实战
  2. 平面上的点和直线上的点一样多
  3. 《机器学习实践应用》书中源代码
  4. PHP实现图片马赛克效果
  5. step1 . day2:Linux系统基础知识
  6. Date日期类型的绑定
  7. linux系统性能测试之虚拟内存管理篇
  8. wap(dopra linux )命令,运营商定制的华为光猫Telnet命令恢复华为界面
  9. 初等数论及其应用——中国剩余定理
  10. mips linux gcc mingw,gcc
  11. 【ruby】ruby学习笔记之--环境搭建
  12. 软考信息系统项目管理师知识点总结1
  13. 《对比Excel,轻松学习Python数据分析》读书笔记------数据可视化
  14. python中match方法中r什么意思_什么是pythonre.match函数?(实例解析)
  15. python 合并加速mp4文件(含对文件、目录的处理)
  16. BIGEMAP如何发布百度离线地图及二次开发API
  17. 【OH】SET System Variable Summary SQLPLUS 系统变量设置
  18. php黄金搭档_动画电影电子游戏的搭档实际上很棒
  19. 堆与栈区别,以及分配内存的快慢
  20. 计算机网络-测试1-概述

热门文章

  1. java字符串怎么拼接字符串_Java中String使用+ 拼接字符串的原理是什么?
  2. JAVA加勒比_【JAVA】synchronized关键字
  3. weblogic启动项目失败查看_weblogic启动报错常见错误解决办法
  4. 分享:一篇webpack配置基础绝好文章
  5. android优化(json工具,message新建/传递,avtivity深入学习视频)
  6. 字符串处理 百度之星资格赛 1002 列变位法解密
  7. TurboMail邮件系统为防垃圾邮件盗号提供专业方案
  8. ArcGIS 9在WIN XP 和 WIN 2003 系统下安装部分动态库不能注册
  9. C++11 原生字符串
  10. Objective-c编程语言(一):The Objective-C Programming Language:Introduction