Celery基本使用

参考Celery 全面学习笔记

一、介绍

1、简介

Celery是一个功能完备即插即用的任务队列。它使得我们不需要考虑复杂的问题,使用非常简单。 celery适用异步处理问题,当发送邮件、或者文件上传, 图像处理等等一些比较耗时的操作,我们可将其异步执行,这样用户不需要等待很久,提高用户体验。 celery的特点是:

  • 简单,易于使用和维护,有丰富的文档。
  • 高效,单个celery进程每分钟可以处理数百万个任务。
  • 灵活,celery中几乎每个部分都可以自定义扩展。

2、概念

在这里插入图片描述
在这里插入图片描述

2.1 Brokers

brokers 中文意思为中间人,在这里就是指任务队列本身,Celery 扮演生产者和消费者的角色,brokers 就是生产者和消费者存放/拿取产品的地方(队列)

常见的 brokers 有 rabbitmq、redis、Zookeeper 等

2.2 Result Stores / backend

顾名思义就是结果储存的地方,队列中的任务运行完后的结果或者状态需要被任务发送者知道,那么就需要一个地方储存这些结果,就是 Result Stores 了

常见的 backend 有 redis、Memcached 甚至常用的数据都可以。

2.3 Workers

就是 Celery 中的工作者,类似与生产/消费模型中的消费者,其从队列中取出任务并执行。Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。

2.4 Tasks

就是我们想在队列中进行的任务咯,一般由用户、触发器或其他操作将任务入队,然后交由 workers 进行处理。

理解以上概念后我们就可以快速实现一个队列的操作:

3、安装

首先需要安装包,celery 是 4.0 及以上版本请确保 python 的 redis 库版本在 2.10.4 及以上,否则会出现 redis 连接 timeout 的错误。最好使用Linux环境,我再windows下感觉一直报错就换Linux了。python3.6的话celery我装最新的结果吧python给我更新成3.7的了,后面装了celery4.2的 版本。

pip install Celery
pip install redis

二、简单使用

1、实例化Celery和 定义任务函数。

新建一个包,然后新建一个tasks.py文件。这里我们用 redis 当做 celery 的 broker 和 backend。

#tasks.py
from celery import Celery

app = Celery('tasks', broker='redis://127.0.0.1:6379/0', backend='redis://127.0.0.1:6379/0')

@app.task   #普通函数装饰为 celery task
def add(x, y):
    print("任务函数正在执行....")
    return x + y

2、运行celery服务。

在 tasks.py 所在目录下运行:

创建一个worker,意思是运行 tasks.py 中任务集合的 worker 进行工作(当然此时broker中还没有任务,worker此时相当于待命状态)。
这里,-A 表示我们的程序的模块名称,worker 表示启动一个执行单元,-l 是批 -level,表示打印的日志级别。可以使用 celery –help 命令来查看celery命令的帮助文档。

celery -A tasks worker --loglevel=info

可以看到启动成功了
在这里插入图片描述
当一个worker启动时,它会产生一定数量的子进程.这些进程的默认数量等于该计算机上的核心数nproc --all,你可以自己指定,例如:

celery -A proj worker --loglevel=INFO --concurrency=2

如果要启动多个worker,可以另开窗口执行多次的启动命令,多个worker会竞争执行任务

3、调用任务

新建一个文件trigger.py,

#trigger.py
from tasks import add

if __name__ == '__main__':
    result = add.delay(4, 4) #不要直接 add(4, 4),这里需要用 celery 提供的接口 delay 进行调用
    print(result.ready())#判断是否执行完成,如果执行完成返回True,未完成返回False
    print(result.id) #获取执行的任务id
    print(result.get())  #获取执行的结果

然后执行trigger.py,查看celery启动的页面,可以看到有接收到任务并执行celery-task-meta-78fe3bc6-58d9-4173-836c-e69bdef7a7b2
在这里插入图片描述
然后查看redis中有执行结果
在这里插入图片描述
在某些特定情境下,我们会先发起celery任务,过一段时间之后再获取其执行结果,而不用一直等待。
代码如下:

from celery.result import AsyncResult
AsyncResult(task_id).get()

如果有引入tasks文件函数失败的可以看下面

工作中经常会碰见在pycharm中写python代码,送到服务器上去运行的情况,如果要调用同级目录的文件,linux服务器上直接import就可以了,但是在pycharm中会报错。
实际上只需要在pycharm中简单配置一下,就可以解决这个问题,右击调用文件所在的包 然后选择:
Mark Directory as->Sources Root

在这里插入图片描述

4、配置信息也可以直接在app中设置

from celery import Celery
app = Celery('tasks')

app.conf.update(
    result_backend='redis://127.0.0.1:6379/0',
    broker_url='redis://127.0.0.1:6379/0',
)

@app.task
def add(x, y):
    print("任务函数正在执行....")
    return x + y

5、配置信息也可以通过专有的配置模块来配置

在tasks.py模块 同级目录下创建配置模块celeryconfig.py

result_backend = 'redis://:password@127.0.0.1:6379/0'
broker_url = 'redis://:password@127.0.0.1:6379/0'

tasks.py文件修改为:

from celery import Celery
import celeryconfig
 
# 我们这里案例使用redis作为broker
app = Celery('tasks')
 
# 从单独的配置模块中加载配置
app.config_from_object('celeryconfig')

三、Celery创建多个队列,将任务分配至不同队列

参考 https://www.dazhuanlan.com/2019/12/17/5df83d3ff2c96/

  • Celery 默认使用名为 celery 的队列 (可以通过 CELERY_DEFAULT_QUEUE 修改) 来存放任务., 因为在进行任务发送时, 如果没有指明队列, 将默认发送至队列名称为celery的队列中。

  • 我们可以使用 优先级不同的队列 来确保高优先级的任务优先执行.

在这里插入图片描述

1、定义一个queuetasks.py文件,实例化一个Celery app ,以及定义一些Task函数

from celery import Celery
import time

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


@app.task
def task1(x, y):
    print("任务task1函数正在执行....")
    time.sleep(10)
    return x + y



@app.task
def task2(x, y):
    print("任务task2函数正在执行....")
    time.sleep(20)
    return x - y

@app.task
def task3(x, y):
    print("任务task3函数正在执行....")
    time.sleep(20)
    return x * y

2、定义一个celeryconfig.py文件,用于配置一些路由、队列等信息。

  • task_queues中定义队列信息,在task_routes中设置任务对应的执行队列,(在有些版本参数可能是CELERY_QUEUES和CELERY_ROUTES)

  • 例如下面指定了queuetasks.task1这个方法由app_task1这个队列执行,queuetasks.task2app_task2队列执行

result_backend = 'redis://127.0.0.1:6379/0'
broker_url = 'redis://127.0.0.1:6379/0'


from kombu import Exchange, Queue

#定义队列
task_queues = (
    #Queue('default', exchange=Exchange('default'), routing_key='default'),
    Queue('app_task1', exchange=Exchange('app_task1'), routing_key='app_task1'),
    Queue('app_task2', exchange=Exchange('app_task2'), routing_key='app_task2'),
)

#指定了`queuetasks.task1`这个方法由`app_task1`这个队列执行,`queuetasks.task2`由`app_task2`队列执行
task_routes = {
    'queuetasks.task1': {'queue': 'app_task1', 'routing_key': 'app_task1'},
    'queuetasks.task2': {'queue': 'app_task2', 'routing_key': 'app_task2'}
}

3、定义queuetrigger.py执行前面定义的三个函数任务

#queuetrigger.py

from queuetasks  import task1,task2,task3

if __name__ == '__main__':
    result1 = task1.delay(40, 20)
    print(result1.id)

    result2 = task2.delay(40, 20)
    print(result2.id)

    result3 = task3.delay(40, 20)
    print(result3.id)


4、在启动worker时指定该worker执行哪一个queue中的任务。如果没有加-Q参数,启动的worker会监听task_queues 中配置的所有队里中的任务,但是不会监听默认的celery队列,所以默认的celery队列还是需要独立起的。

如下前两个worker,分别执行app_task1和app_task2队列中的任务,第三个执行celery默认队列中的任务。

celery -A queuetasks worker -l info -Q app_task1
celery -A queuetasks worker -l info -Q app_task2
celery -A queuetasks worker -l info -Q celery

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5、执行queuetrigger.py中的任务,多执行几次观察

我这里执行了四次,每次都能返回taskid
在这里插入图片描述
查看启动的页面,有执行任务

task1在app_task1队列中执行
在这里插入图片描述
task2在app_task2队列中执行
在这里插入图片描述
task3在默认的celery队列中执行
在这里插入图片描述

注意:如果没有启动celery的woker就直接执行task函数,也会可以返回taskid,celery会将任务先存放在队列中,等worker一启动就开始消费。

四、Django中使用Celery

在celerydemo这个django项目中创建一个新的app djangocelerydemo

python manage.py startapp djangocelerydemo

在这里插入图片描述

1、在根项目中新建celery.py

from __future__ import absolute_import, unicode_literals
import os
from celery import Celery

# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'celerydemo.settings')

app = Celery('celery_task')

# Using a string here means the worker don't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
#   should have a `CELERY_` prefix.
# 可以将celery的配置信息放在django的setting.py文件中,要以CELERY_为开头
app.config_from_object('django.conf:settings', namespace='CELERY')

# Load task modules from all registered Django app configs.
#自动发现django应用里的celery任务
app.autodiscover_tasks()

@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))

2、在setting.py中设置配置信息

#for celery
CELERY_BROKER_URL = 'redis://:password@127.0.0.1:6379/0'
CELERY_RESULT_BACKEND = 'redis://:password@127.0.0.1:6379/0'

#默认celery的时区为UTC,如果要在django项目中将celery定时任务配置为根据本地时区触发,则需要修改
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_ENABLE_UTC = False

3、在根项目/__init__.py文件中增加如下内容,确保django启动的时候这个app能够被加载到

from __future__ import absolute_import

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ['celery_app']

4、在djangocelerydemo应用中创建tasks.py模块,注意tasks.py必须建在各app的根目录下,且只能叫tasks.py,不能随意命名。

  • 如果使用了多个装饰器那么需要task装饰器在最后即在最上面,一般情况使用的是从celeryapp中引入的app作为的装饰器:app.task()
  • 如果是django那种在app中定义的task则需要使用@shared_task,由shared_task装饰的任务可以被任何app调用
import time
from celery import shared_task

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

5、在views.py模块中创建视图index,引用使用这个tasks异步处理

from django.http import HttpResponse
from djangocelerydemo.tasks import my_task


def index(request):
    # 将my_task任务加入到celery队列中
    # 如果my_task函数有参数,可通过delay()传递
    # 例如 my_task(a, b), my_task.delay(10, 20)
    my_task.delay()

    return HttpResponse("<h1>服务器返回响应内容!</h1>")

6、在celeydemo/urls.py配置视图路由:

from djangocelerydemo.views import index
from django.conf.urls import re_path

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

7、创建worker等待处理celery队列中任务, 在最外层目录终端执行命令,这里-A后面跟的项目名

#如果Ansible作为celery任务时,Celery执行Ansible的task任务时没有结果,需要export
export PYTHONOPTIMIZE=1
celery -A celerydemo worker --loglevel=info

在这里插入图片描述

8、启动django测试服务器

9、访问路由进行测试

在这里插入图片描述

在这里插入图片描述

10、后台启动多个worker,用下面的命令可以在后台启动,

celery multi start w1 -A celerydemo --loglevel=info
celery multi start w2 -A celerydemo --loglevel=info
celery multi start w3 -A celerydemo --loglevel=info

在这里插入图片描述
如果要停止可以将start改为stop
在这里插入图片描述

在这里插入图片描述

五、Celery定时任务

1、新建一个scheduletasks.py文件

from celery import Celery
from celery.schedules import crontab

app = Celery('tasks', broker='redis://127.0.0.1:6379/0', backend='redis://127.0.0.1:6379/0')

@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
    # Calls test('hello') every 10 seconds.
    sender.add_periodic_task(10.0, test.s('hello'), name='add every 10')

    # Calls test('world') every 30 seconds
    sender.add_periodic_task(30.0, test.s('world'), expires=10)

    # Executes every Monday morning at 7:30 a.m.
    sender.add_periodic_task(
        crontab(hour=7, minute=30, day_of_week=1),
        test.s('Happy Mondays!'),
    )

@app.task
def test(arg):
    print(arg)

注意:如果只想在django中简单使用定时任务,可以把定时任务的配置setup_periodic_tasks和执行函数放在主项目的celery.py中,然后在setup_periodic_tasks中引用执行函数,最后再启动worker和调度器beat进程。如果执行函数没有放一起,启动后虽然定时任务能执行,但异步任务执行不了。如果是要复杂一下的界面化,可以看下面的django-celery-beat一节。

2、启动worker和任务调度器beat进程

启动celery worker进程

celery -A scheduletasks worker --loglevel=info

启动定时任务Celery Beat进程

 celery -A scheduletasks beat --loglevel=info

可以看到两个进程中都有输出任务执行的信息
在这里插入图片描述

在这里插入图片描述

3、上面是通过调用函数添加定时任务,也可以像写配置文件 一样的形式添加, 下面是每30s执行的任务

用@app.on_after_configure.connect装饰器,是把计划写死在一个函数里了。似乎无法动态添加新任务。不过好处是结构比较清晰。

而后一种方法,只要更新一下 app.conf.beat_schedule 这个字典里的配置信息,然后重启Beat就能生效了。

app.conf.beat_schedule = {
    'add-every-30-seconds': {
        'task': 'tasks.add',
        'schedule': 30.0,
        'args': (16, 16)
    },
}
app.conf.timezone = 'UTC'

4、更复杂的定时配置

上面的定时任务比较简单,只是每多少s执行一个任务,但如果你想要每周一三五的早上8点给你发邮件怎么办呢?哈,其实也简单,用crontab功能,跟linux自带的crontab功能是一样的,可以个性化定制任务执行时间

下面这条意思是每周1的早上7.30执行tasks.add任务

from celery.schedules import crontab

app.conf.beat_schedule = {
    # Executes every Monday morning at 7:30 a.m.
    'add-every-monday-morning': {
        'task': 'tasks.add',
        'schedule': crontab(hour=7, minute=30, day_of_week=1),
        'args': (16, 16),
    },
}

六、Django结合Celery定时任务(使用django-celery-beat及配套界面)

参考django-celery-beat的使用
1、安装django-celery-beat

 pip install django-celery-beat

2、将django_celery_beat模块添加到INSTALLED_APPSDjango项目中settings.py

INSTALLED_APPS = [
    ...
    'django_celery_beat',
]

3、应用Django数据库迁移,以便创建必要的表:

python manage.py migrate

迁移之后生产几个表:

django_celery_beat.models.PeriodicTask # 此模型定义要运行的单个周期性任务。
django_celery_beat.models.IntervalSchedule # 以特定间隔(例如,每5秒)运行的计划。
django_celery_beat.models.CrontabSchedule
# 与像在cron项领域的时间表 分钟小时日的一周 DAY_OF_MONTH month_of_year
django_celery_beat.models.PeriodicTasks # 此模型仅用作索引以跟踪计划何时更改

django_celery_beat_solarschedule # 根据太阳升起降落定制任务
django_celery_beat_clockedschedule # 此模型存放已经关闭的任务

4、分别启动worker与beat

celery  -A celerydemo worker -l info

在这里插入图片描述

celery -A celerydemo beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler

注意:这里如果是分布式部署celery,有多台机器的话,worker每台机器都起,beat只需要在一台机器上起,不然定时任务会执行多次
在这里插入图片描述

如果django中要后台启动或关闭celery,使用如下命令

启动:celery multi start w1 -A celerydemo -l info --logfile = celerylog.log --pidfile = celerypid.pid

停止:celery multi stop w1 -A celerydemo -l info
重启:celery multi restart w1 -A celerydemo -l info

5、创建一个admin用户,然后登陆

python manage.py createsuperuser

可以看到有几张表
在这里插入图片描述

6、新建一个定时任务测试
我这里选了前面的my_task这个任务,5s执行一次,
在这里插入图片描述
然后查看worker输出,有执行
在这里插入图片描述

beat调度器也有输出
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值