Celery
1. 简介
Celery - 中文名翻译叫芹菜,是一种分布式的任务队列(Distribute Task Queue)
Celery is a simple, flexible, and reliable distribute system to process vast amounts of message, while providing operations with the tools required to miantain such a system. --摘自官方文档
Celery是一个简单的、灵活的并且可靠的分布式系统,用来处理大量消息任务,同时为操作人员提供维护这样一个
2. 安装使用
Celery需要依赖中间件做数据的存储,一般采用RabbitMQ或者Redis居多,这里简单的介绍采用Redis形式存储Celery的任务。
首先需要安装Celery
# 安装celery即可
pip install celery
# 因为是redis,需要安装redis
pip install redis
注意:直接安装celery会默认安装最新版本,可能有些windows系统不适配,建议安装 **pip install celery==3.1.12**
版本,以更好适配
编写程序,采用配置文件来将celery的配置统一编写,一起加载进celery中
celery_config.py
# ip地址可以是虚拟机的地址或者是你购买的云服务器的地址
BROKER_URL = 'redis://192.168.233.129' # 使用Redis作为消息代理
CELERY_RESULT_BACKEND = 'redis://192.168.233.129:6379/0' # 把任务结果存在了Redis
CELERY_TASK_SERIALIZER = 'msgpack' # 任务序列化和反序列化使用msgpack方案--启动时候会提醒安装对应的库
CELERY_RESULT_SERIALIZER = 'json' # 读取任务结果一般性能要求不高,所以使用了可读性更好的JSON
CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24 # 任务过期时间
CELERY_ACCEPT_CONTENT = ['json', 'msgpack'] # 指定接受的内容类型
execute_celery.py
from celery import Celery
# set the celery name and broker config
# app = Celery('task', broker="amqp://192.168.233.129")
app = Celery('cle', include=['cle.tasks'])
app.config_from_object('cle.celery_config') # read config and load config
# the task of celery
@app.task
def add(x, y):
return x + y
celery_test.py
import time
from cle.execute_celery import add
t1 = time.time()
r1 = add.delay(1, 2)
r2 = add.delay(2, 4)
r3 = add.delay(3, 6)
r4 = add.delay(4, 8)
r5 = add.delay(5, 10)
r_list = [r1, r2, r3, r4, r5]
for r in r_list:
while not r.ready():
pass
print(r.result)
t2 = time.time()
print('共耗时:%s' % str(t2-t1))
执行celery
celery -A cle.celery_test worker -l info
# 具体命令帮助查看
celery worker --help
此时在redis中可以看到
# 启动redis,进入到具体的db,查看所有的key
keys *
# 查看key的类型
type key_1
# 因为采用的是list,所以需要采用lrange的方式读取数据
lrange celery 0 -1 # 表示读取所有数据
此时可以看到redis库中存储的任务数据
3. Application
使用之前需要实例化一个celery,这个实例叫做一个应用Application简称为app。
应用是线程安全的,所以多个有不同配置的celery应用并且任务可以在同一个处理空间中共存。
from celery import Celery
app = Celery()
print app
# output: <Celery __main__ at 0x100bdecd0>
输出显示了应用程序的文本表示,包括当前app class、当前主模块的名称和对象的内存地址。
3.1 Main Name
上面的输出中有一个是最重要的,就是主模块的名称。
当你发送一个任务消息到celery中,这个消息并不会包含任何的源码,仅仅包含你想要执行的任务的名称。这个工作机制类似于在互联网上的工作方式:每个工作人员都维护一个任务名称与其实际功能的映射,称为任务注册表。
无论你什么时候定义一个task,这个task总是会被加入到本地注册表中。
from celery import Celery
if __name__ == '__main__':
app = Celery()
@app.task
def add(x, y):
return x + y
print add
# <@task: __main__.add of __main__ at 0x106678d10>
Main Name的话都是以模块名称来结果,比如你在tasks.py
中定义了一个@app.task
名称是add
,那么当你在其他模块中调用的时候,首先需要导入当前tasks
,其次调用这个模块,Main Name为tasks.add
。
3.2 Configuration
配置,通过修改配置中的一些选项可以改变Celery的工作方式。这些选项可以被直接设置在app实例中,或者使用专门的配置模块来实现。
config_from_object
即来源于对象的配置。可以实现从配置对象中加载配置。可以是一个配置模块或者是带有配置属性的任何对象。
from celery import Celery
app = Celery()
# 1、从带有配置属性的配置文件中加载
app.config_from_object('celeryconfig')
# 2、传递实际的模块对象
app.config_from_object(celeryconfig)
# 3、使用配置类或者配置对象
class Config:
enable_utc = True
timezone = 'Europe/London'
app.config_from_object(Config)
# 或者 app.config_from_object('module:Config')
# 4、从环境变量中获取配置 config_from_envvar
import os
# 设置环境变量
os.environ.setdefault('CELERY_CONFIG_MODULE','celeryconfig')
app.config_from_envvar('CELERY_CONFIG_MODULE')
3.3 Laziness
懒加载,应用实例是懒加载的,意味着在它被实际使用之前是不会被加载的。
4.4 Breaking the chain
虽然可能依赖与当前设置的应用程序,但最佳的实践是始终将应用程序实例传递给任何需要它的对象。
这被称作app chain,虽然创建一个调用链取决于这个app被传递。
from celery import current_app
class Scheduler:
# 为了保持调用链,应该将app作为参数进行传递
def __init__(self, app):
self.app = app
def run(self):
app = current_app
3.5 Abstract Tasks
所有任务使用task()
创建都会从应用基类处继承Task
类。
但可以指定task
继承自指定的基类。
@app.task(base=MyTask)
def add(x, y):
return x + y
4. Django整合
版本requirements.txt文件如下,可以使用pip freeze > requirements.txt
导出
amqp==2.6.1
billiard==3.6.4.0
celery==4.4.7
configparser==4.0.2
contextlib2==0.6.0.post1
Django==1.11.29
importlib-metadata==2.1.1
kombu==4.6.11
msgpack==1.0.2
pathlib2==2.3.5
pytz==2021.1
redis==3.5.3
scandir==1.10.0
six==1.16.0
vine==1.3.0
zipp==1.2.0
与Django的整合比较简单,首先需要创建Django项目,同时django-admin startapp celeryapp1
创建一个celery的项目。
修改settings.py
# config from celery
CELERY_TASK_TRACK_STARTED = True
CELERY_BROKER_URL = 'redis://localhost:6379/0' # the message agent
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0' # the result store
CELERY_TASK_SERIALIZER = 'json' # the serializer method
CELERY_RESULT_SERIALIZER = 'json' # result serializer method
# the task expire time
CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24
# the specify task can accept the type of content
CELERY_ACCEPT_CONTENT = ["json"]
编写tasks.py
文件
from celery import shared_task
@shared_task
def add(x, y):
return x + y
@shared_task
def mul(x, y):
return x * y
@shared_task
def xsum(numbers):
return sum(numbers)
在celeryapp1中views.py
中写一个调用任务的方法
def celery(request):
result = tasks.add.delay(4, 4) # called the celery task
data = {"status": "success", "task_id": result.task_id}
return JsonResponse(data)
同样在urls.py
中也需要配置
import celeryapp1.views
urlpatterns = [
url(r'^celery/', celeryapp1.views.celery)
]
4.1 redis查看具体的结果
5. 小结
Celery是在工作中需要用到,知道是个任务队列,但没有使用过,所以花了点时间简单看了下,Celery还有存储orm的能力,可以看官方文档Celery官方文档,如果需要存储orm记录的话,而且对于延迟的执行任务,Celery也支持,但是目前好像延迟任务执行的RundeckRundeck官方文档用的比较多,感兴趣的可以去看看。
参考文档
- https://docs.celeryproject.org/en/stable/index.html
Keep thinking, keep coding! 2021-06-14写于深圳