Django中celery机制的使用总结

什么是任务队列

任务队列是一种跨线程、跨机器工作的一种机制。

任务队列中包含称作任务的工作单元。有专门的工作进程持续不断的监视任务队列,并从中获得新的任务并处理。

什么是 celery

celery 是一款基于 python 的异步任务处理框架

celery 通过消息进行通信,通常使用一个叫 Broker(中间人) 来协client (任务的发出者)worker (任务的处理者)。 clients 发出消息到队列中,broker 将队列中的信息派发给 worker 来处理。

一个 celery 系统可以包含很多的 worker 和 broker,可增强横向扩展性和高可用性能。
任务队列模型图

celery 的安装

(1) 使用 pip 安装:

pip install celery 

在安装出现超时现象时,可切换国内源。

(2)从官方下载安装包: https://pypi.python.org/pypi/celery/

task queue

celery 作为框架,也需要一种解决消息的发送和接收的方式,我们把这种用来存储消息的中间装置叫做 message broker,也可以叫做消息中间人。

可选的 broken 方案

1.RabbitMQ

RabbitMQ 是一个功能完备,稳定的并且易于安装的 broker. 它是生产环境中最优的选择。使用RabbitMQ的细节参照以下链接: http://docs.celeryproject.org/en/latest/getting-started/brokers/rabbitmq.html#broker-rabbitmq

如果我们使用的是 Ubuntu 或者 Debian 发行版的 Linux,可以直接通过下面的命令安装 RabbitMQ: sudo apt-get install rabbitmq-server 安装完毕之后,RabbitMQ-server 服务器就已经在后台运行。如果您用的并不是Ubuntu或Debian, 可以在以下网址: http://www.rabbitmq.com/download.html 去查找自己所需要的版本软件。

2.Redis

Redis 也是一款功能完备的broker可选项,但是其更可能因意外中断或者电源故障导致数据丢失的情况。 关于是有那个 Redis 作为 Broker,可访下面网址: http://docs.celeryproject.org/en/latest/getting-started/brokers/redis.html#broker-redis

celery 的使用

使用 celery 第一件要做的最为重要的事情是需要先创建一个 Celery 实例,我们一般叫做 celery 应用,或者更简单直接叫做一个 app。app 应用是我们使用 celery 所有功能的入口,比如创建任务,管理任务等,在使用 celery 的时候,app 必须能够被其他的模块导入。

首先我们创建 tasks.py ,内容为:

from celery import Celery

# 我们这里案例使用redis作为broker
app = Celery('demo', broker='redis://127.0.0.1/8')


# 创建任务函数
@app.task
def my_task():
    print("任务函数正在执行.")

Celery 第一个参数是给其设定一个名字, 第二参数我们设定一个中间人 broker , 在这里我们使用 Redis 作为中间人。my_task 函数是我们编写的一个任务函数, 通过加上装饰器 app.task, 将其注册到 broker 的队列中。

然后我们创建一个 worker:
cd 到celery 的同级目录中,执行命令:

celery -A tasks worker --loglevel=info 

注意在执行该命令前,需要首先安装 redis。

接下来我们开始调用任务:
我们需要将任务加入到 broker 队列中,以便刚才我们创建的 celery worker 服务器能够从队列中取出任务并执行。如何将任务函数加入到队列中,可使用 delay()。

从 tasks.py 同级目录中进入 python 终端, 执行如下代码:

from tasks import my_task 
my_task.delay() 

调用任务效果图
我们通过 worker 的控制台,可以看到我们的任务被 worker处理。调用一个任务函数,将会返回一个 AsyncResult 对象,这个对象可以用来检查任务的状态或者获得任务的返回值。
查看任务的执行结果

存储任务的执行结果

如果我们想跟踪任务的状态,Celery 需要将结果保存到某个地方。有几种保存的方案可选: SQLAlchemy、Django ORM、Memcached、 Redis、RPC (RabbitMQ/AMQP)。

例子我们仍然使用 Redis 作为存储结果的方案,任务结果存储配置我们通过 Celery 的 backend 参数来设定。我们创建 tasks2 模块修改如下:

from celery import Celery

# 我们这里案例使用 redis 作为 broker
app = Celery('demo',
             backend='redis://127.0.0.1:6379/8',
             broker='redis://127.0.0.1:6379/9')


@app.task
def my_task(a, b):
    print("任务函数正在执行.")
    return a + b

开启终端,尝试将任务加入到 broker 队列中。
导入 tasks2 模块的任务
查看任务的状态、task_id 等信息

开启针对 tasks2 模块的 worker 服务器:

celery -A tasks2 worker --loglevel=info 

再次查看任务的状态和运行结果:
开启worker服务器之后任务被顺利执行
同时可以看到, 我们给 Celery 增加了 backend 参数,指定redis 作为结果存储,并将任务函数修改为两个参数,并且有返回值。

更多关于 result 对象信息,请参阅下列网址: http://docs.celeryproject.org/en/latest/reference/celery.result.html#module-celery.result

配置 celery app 的不同方式

创建 tasks3 模块来展示三种配置 app 的方式:

from celery import Celery

# # (1) 在初始化 app 的时候进行配置
# app = Celery('demo',
#              backend='redis://127.0.0.1:6379/8',
#              broker='redis://127.0.0.1:6379/9')


# # (2) 通过 app 的 conf 属性进行配置
# app = Celery('demo')
# app.conf.update(
#     result_backend='redis://127.0.0.1:6379/8',
#     broker_url='redis://127.0.0.1:6379/9',
# )


# (3) 通过专用的配置文件来加载配置
app = Celery('demo')
'''
下面我们在tasks.py模块 同级目录下创建配置模块celeryconfig.py:
result_backend = 'redis://127.0.0.1:6379/8'
broker_url = 'redis://127.0.0.1:6379/9'
'''
app.config_from_object('celeryconfig')


@app.task
def my_task(a, b):
    print("任务函数正在执行.")
    return a + b

更多配置: http://docs.celeryproject.org/en/latest/userguide/configuration.html#configuration

在项目中使用 celery 的结构配置

创建一个项目 mypro, 结构如下:
项目目录结构
其中创建 app 的文件被命名为 celery.py:

# celery.py 
from celery import Celery

app = Celery('mypro')
app.config_from_object('mypro.celeryconfig')

# 自动搜索任务
app.autodiscover_tasks(['mypro'])

app 的配置文件为 celeryconfig.py:

# celeryconfig.py 
BROKER_URL = 'redis://127.0.0.1:6379/8'
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/9' 

任务函数写在 tasks.py 中:

#  tasks.py 
from mypro.celery import app as celery_app


# 创建任务函数
@celery_app.task
def my_task1():
    print("任务函数(my_task1)正在执行.")
    return "task1 done"


@celery_app.task
def my_task2():
    print("任务函数(my_task2)正在执行.")
    return "task2 done"


@celery_app.task
def my_task3():
    print("任务函数(my_task3)正在执行.")
    return 'task3 done'

cd 到 mypro 同级目录, 启动 worker 服务器:

celery -A mypro worker -l info 

启动项目的worker服务器

celery 任务的调用方式

(1) 如果我们直接执行任务函数,将会直接执行此函数在当前进程中,并不会向broker发送任何消息:
导入任务模块
直接阻塞调用任务

(2) 调用任务,可使用 delay() 方法: my_task.delay(2, 2)
也可以使用 apply_async() 方法,该方法可让我们设置一些任务执行的参数,例如,任务多久之后才执行,任务被发送到那个队列中等等.

my_task.apply_async((2, 2), queue='my_queue', countdown=10)

任务 my_task 将会被发送到 my_queue 队列中,并且在发送10秒之后执行。
无论是 delay() 还是 apply_async() 方式都会返回 AsyncResult 对象,方便跟踪任务执行状态,但需要我们配置r esult_backend.
每一个被调用的任务都会被分配一个 ID,我们叫 Task ID.

签名 signature

有时我们并不想简单的将任务发送到队列中,我们想将一个任务函数(由参数和执行选项组成) 作为一个参数传递给另外一个函数中,为了实现此目标,Celery 使用一种叫做 signature 的东西。

一个 signature 包装了一个参数和执行选项的单个任务调用。我们可将这个 signature 传递给函数。

首先我们新增一个任务函数,是我们自定义的加法:
自定义的加法任务函数
测试直接调用和封装为 signature ,汇入参数再调用的效果:
测试不同的调用效果

运行结果:
测试调用的运行结果

Primitives

primitives 本身可认为是 signature 对象,因此它们可以以多种方式组合成复杂的工作流程。primitives 如下:

  • (1) group: 一组任务并行执行,返回一组返回值,并可以按顺序检索返回值。
    测试 group 的使用
  • (2) chain: 任务一个一个执行,一个执行完将执行return结果传递给下一个任务函数:
    测试chain的使用
    运行结果:
    chain test 运行结果

Routing

假如我们有两个 worker ,一个 worker 专门用来处理邮件发送任务和图像处理任务,一个 worker 专门用来处理文件上传任务。

我们创建两个队列,一个专门用于存储邮件任务队列和图像处理,一个用来存储文件上传任务队列。

Celery 支持 AMQP(Advanced Message Queue) 所有的路由功能,我们也可以使用简单的路由设置将指定的任务发送到指定的队列中。

我们需要配置在 celeryconfig.py 模块中配置 CELERY_ROUTES 项, tasks.py 模块修改如下:
指定任务队列
我们可以开启两个任务服务器,分别处理自己对应注册的任务:

celery -A mypro worker --loglevel=info -Q queue1
celery -A mypro worker --loglevel=info -Q queue2 

我们也可以在一个任务服务器中开启两个队列:

celery -A mypro worker --loglevel=info -Q queue1,queue2

最后,我们还可以在注册任务时主动指定自己所想要使用的队列。

ret = my_add.apply_async(args=(1, 2), queue='queue1')

测试时我们开启三个任务服务器:
开启三个任务服务
运行测试任务, 观察任务进入的队列。
运行测试任务

执行周期任务调度

celery beat 是一个调度器,它可以周期内指定某个 worker 来执行某个任务。如果我们想周期执行某个任务需要增加 beat_schedule 配置信息.  
在 app 所在的文件中增加配置信息:

app.conf.beat_schedule = {
    'every-5-seconds':
        {
            'task': 'mypro.tasks.my_add',
            'schedule': 5.0,
            'args': (16, 16),
        }
}

配置完成之后,在终端启动定时的 worker 服务,
同时使用 --beat 启动周期任务注册服务:

celery -A mypro worker --loglevel=info -Q queue1 --beat

运行效果:
定时运行效果
如果我们想指定在某天某时某分某秒执行某个任务,可以执行cron任务, 增加配置信息如下:

app.conf.beat_schedule = {
    # 'every-5-seconds':
    #     {
    #         'task': 'mypro.tasks.my_add',
    #         'schedule': 5.0,
    #         'args': (16, 16),
    #     },
    # 每周三下午的 2 点 32 分运行一次
    'add-every-wednesday-afternoon': {
            'task': 'mypro.tasks.my_add',
            'schedule': crontab(hour=14, minute=32, day_of_week=3),
            'args': (16, 16),
    },

}

注意在指定某个时间点运行任务时,需要为 celery 配置所在的时区。

app.conf.timezone = 'Asia/Shanghai'

更多crontab例子:
http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html

将 worker 服务与 beat 服务分开

单独开启一个celery beat服务:(注意使用 sudo 权限)

sudo celery -A mypro -l info beat

celery 需要保存上次任务运行的时间在数据文件中,文件在当前目录下名字叫celerybeat-schedule. beat, 需要访问此文件:

sudo celery -A mypro -l info beat -s /Users/furuiyang/gitzip/DjangoDemos/celery_learn/celerybeat-schedule

保存定时任务信息的celery文件

在 django 项目中使用 celery

首先,我们来创建 django app:

pipinstall django 
django-admin startproject celery_demo 
python manage.py startapp demo

接着,我们在 celery_demo 模块中创建 celery.py 模块.
文件目录
内容:

# celery.py 
from celery import Celery
from django.conf import settings
import os

# 为celery设置环境变量
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'celery_demo.settings')

# 创建应用
app = Celery("demo")
# 配置应用
app.conf.update(
    # 配置broker, 这里我们用redis作为broker
    BROKER_URL='redis://127.0.0.1:6379/8',
)

# 设置app自动加载任务
# 从已经安装的app中查找任务
app.autodiscover_tasks(settings.INSTALLED_APPS)

接着,我们来创建一个任务:
在 demo app 中创建 tasks.py 文件:

# tasks.py
from celery_demo.celery import app
import time


# 加上app对象的task装饰器
# 此函数为任务函数
@app.task
def my_task():
    print("任务开始执行....")
    time.sleep(5)
    print("任务执行结束....")

然后我们来创建启动任务的类前端视图,以及为这个视图配置相应的路由:

# demo.views.py 
from django.http import HttpResponse
from .tasks import my_task


def index(request):
    my_task.delay()
    return HttpResponse("<h1>服务器返回响应内容!</h1>") 
# url.py 
from django.conf.urls import url
from django.contrib import admin
from demo.views import index

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', index),
] 

同时注意在 settings.py 中注册 demo app:

# settings.py 
...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'demo',
] 
...

在 django 根目录下启动 celery 的 worker 服务:

celery -A celery_demo worker -l info 

启动 django 服务并且请求 http://127.0.0.1:8000/
启动django服务
观察任务的执行:
celery 任务执行

在 django 中保存任务执行结果

首先我们需要安装一个新的模块:

 pipinstall django-celery-results

接着我们在项目的 settings 文件中安装此应用:
安装 django-celery-results app
接着我们在 celery.py 文件中增加配置信息:

# celery.py
from celery import Celery
from django.conf import settings
import os

# 为celery设置环境变量
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'celery_demo.settings')

# 创建应用
app = Celery("celery_demo")

# 配置应用
app.conf.update(
    # 配置broker, 这里我们用redis作为broker
    BROKER_URL='redis://127.0.0.1:6379/8',
    # 使用项目数据库存储任务执行结果
    CELERY_RESULT_BACKEND='django-db',
)

# 设置app自动加载任务
# 从已经安装的app中查找任务
app.autodiscover_tasks(settings.INSTALLED_APPS) 

增加执行任务存储的配置信息
然后我们修改下自己的任务函数,使其有返回值:
给任务函数增加返回值
最后执行数据库迁移文件:

python manage.py migrate django_celery_results 

当我们再次注册任务、启动 worker 服务时,就可以在 sqlite 数据库中看到我们任务的记录了:
查看任务记录

在 django 中执行定时任务

首先我们需要安装 celery beat 针对 django 的扩展包:

pip install django_celery_beat 

安装 celery beat 的扩展包
接着,我们需要在 settings 中安装此应用 并且增加对应的配置:
安装新应用
增加配置
由于定时器信息存储在数据库中,我们需要先生成对应表, 对 diango_celery_beat 执行迁移操作,创建对应表:

python manage.py migrate django_celery_beat 

进入后台,创建定时器 以及 针对这个定时器需要启动的任务函数:
任务后台
其中 Crontabs 用于定时某个具体时间执行某个任务的时间,Intervals 用于每隔多久执行任务的事件,具体任务的执行在 Periodic tasks 表中创建。

我们要创建每隔 5 秒执行某个任务,所以在 Intervals 表名后面点击 Add 按钮:
创建定时器
然后在 Periodic tasks 表名后面,点击 Add 按钮,添加任务:
创建任务
最后启动定时任务的 worker 服务和 beat 服务:

celery -A celery_demo worker -l info --beat

任务每隔5秒中就会执行一次,如果配置了存储,那么每次任务执行的结果也会被保存到对应的数据库中。
定时任务的存储结果

代码

https://github.com/furuiyang0715/celery_learn

参考

  • https://www.celerycn.io/yong-hu-zhi-nan/canvas-she-ji-gong-zuo-liu-cheng-canvas-designing-workflows/qian-ming-signatures
  • https://www.youtube.com/watch?v=BbPswIqn2VI
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值