Celery

celery使用redis集群 celery redis cluster_分布式

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)
]

celery使用redis集群 celery redis cluster_分布式_02

4.1 redis查看具体的结果

celery使用redis集群 celery redis cluster_celery使用redis集群_03

5. 小结

Celery是在工作中需要用到,知道是个任务队列,但没有使用过,所以花了点时间简单看了下,Celery还有存储orm的能力,可以看官方文档Celery官方文档,如果需要存储orm记录的话,而且对于延迟的执行任务,Celery也支持,但是目前好像延迟任务执行的RundeckRundeck官方文档用的比较多,感兴趣的可以去看看。

参考文档

Keep thinking, keep coding! 2021-06-14写于深圳