Celery分布式任务队列

Celery官方文档

Clery官方文档中文版

Celery简介

Celery是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列

Celery异步任务框架

Celery提供异步任务框架,主要有以下三大功能:
1. 执行异步任务
2. 执行延迟任务
3. 执行定时任务

1.可以不依赖任何服务器,通过自身命令,启动服务(内部支持socket)

2.celery服务为为其他项目服务提供异步解决任务需求的

:会有两个服务同时运行,一个是项目服务,一个是celery服务,项目服务将需要异步处理的任务交给celery服务,celery就会在需要时异步完成项目的需求

注意

在Windows平台上运行Celery可能会有问题:

Celery is a project with minimal funding, 
so we don’t support Microsoft Windows.
Please don’t open any issues related to that platform.

Celery架构

Celery的架构由三部分组成:

    1. 消息中间件(message broker)、

     2. 任务执行单元(worker)

     3. 任务执行结果存储(task result store)组成

消息中间件:broker(需要使用第三方,redis,rabbitmq)

Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ, Redis等等

任务执行单元:worker(celery提供的)

Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。

任务结果存储:backend(使用第三方)

Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等

使用场景

异步执行:解决耗时任务,将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等

延迟执行:解决延迟任务

定时执行:解决周期(周期)任务,比如每天数据统计

Celery的使用

安装

pip install celery

 Celery的两种结构

1.基本结构  # 所有文件都放在一个py文件当中

2.包管理结构  # 推荐使用包管理,结构更清晰
# 建一个包 包名任意 包内必须有个celery.py文件 文件内书写Celery()产生对象的语句

方式一:基本结构

1.创建一个 celery_task.py 文件,实例化得到app,编写任务(写一个函数...),用装饰器装饰

# celery_task.py

from celery import Celery

# broker = 'redis://:123456@127.0.0.1:6379/1'  # 123456是密码
# backend = 'redis://:123456@127.0.0.1:6379/2'  # 123456是密码
broker = 'redis://127.0.0.1:6379/1'  # broker任务队列 消息中间件
backend = 'redis://127.0.0.1:6379/2'  # 结构存储,执行完的结果存在这

# 1.实例化得到一个对象app 指定broker消息中间件 和 backend结果仓库
app = Celery(__name__,broker=broker,backend=backend)  # app对象的名字,把当前文件的名字给它

# 2.添加任务(就是add函数) 用装饰器@app.task装饰该任务
@app.task
def add(x,y):
    print(x,y)
    return x+y

2.在其他系统 add_task.py 文件中,提交任务(导入任务)

# add_task.py

from celery_task import add

# add(3,4)  # 只是直接执行,不会被添加到broker中
res = add.delay(4,5)  # 向broker中添加一个任务 要用到delay
print(res)  # 得到一个id值: c9168245-8966-4cba-9412-1a57218b7daf


# 只要 add_task.py 一执行,就会向Broker提交一个任务,
# 当Worker启动时,任务执行的结果会被存储到Backend当中

3.启动 worker:需要在 Terminal中 cd 到 celery_task.py 所在的文件夹下

# 用命令来执行
        非Windows系统:
            # 命令:celery -A 模块名 worker -l info
            celery -A celery_task worker -l info
Windows系统: pip3 install eventlet
# Windows下需要借助eventlet # 命令:celery -A 模块名 worker -l info -P eventlet celery -A celery_task worker -l info -P eventlet

注意版本区别: # 3.x及以前: # celery worker -A 模块名 -l info -P eventlet celery worker -A celery_task -l info -P eventlet # 需要切换到指定文件夹 # 4.x及以后: # celery -A 模块名 worker -l info -P eventlet celery -A celery_task worker -l info -P eventlet

4.查看执行的结果,get_result.py

# get_result.py

from celery_task import app
from celery.result import AsyncResult

id = 'eda5822a-6c3e-4f0f-b399-0802226bc3b0'
if __name__ == '__main__':
    asy = AsyncResult(id=id, app=app)
    if asy.successful(): # 顺利执行完了,可以取结果了
        res = asy.get()  # 任务执行的结果
        print(res)
    elif asy.failed():
        print('任务失败')
    elif asy.status == 'PENDING':
        print('任务等待中被执行')
    elif asy.status == 'RETRY':
        print('任务异常后正在重试')
    elif asy.status == 'STARTED':
        print('任务已经开始被执行')

方式二:包管理(推荐,结构清晰)

 1.创建一个包,包内必须有一个名为 celery.py 的文件,将Celery()产生对象的语句放在该文件中

包格式如下:

celery_task  # 包名
    __init__.py
    celery.py      # 必须叫这个名字
    task1.py   # 一堆任务
    task2.py   # 一堆任务

任务提交-正常情况写在项目中.py
结果查看--正常情况写在项目中.py

celery_task/celery.py 文件中内容如下:

# celery.py

from celery import Celery


broker = 'redis://127.0.0.1:6379/1'  # broker任务队列 消息中间件
backend = 'redis://127.0.0.1:6379/2'  # 结构存储,执行完的结果存在这

# 1.实例化得到一个对象app 指定broker消息中间件 和 backend结果仓库
app = Celery(__name__,broker=broker,backend=backend,
             include=['celery_task.task1','celery_task.task2'])

2.书写任务,可以有很多

 celery_task/task1.py文件中:

# task1.py

from .celery import app  # 导入app

@app.task
def send_code(phone,code):
    print('%s发送了验证码%s'%(phone,code))
    return '号码:%s,验证码:%s'%(phone,code)

 celery_task/task2.py文件中:

# task2.py

from .celery import app  # 导入app

@app.task
def upload(name,img):
    print('%s更新了照片%s'%(name,img))
    return '%s更新了照片%s'%(name,img)

3.启动 worker   需要cd到 celery 包所在的路径(scripts路径下)

# 用命令来执行
        非Windows系统:
            # 命令:celery -A 包名 worker -l info
            celery -A celery_task worker -l info

        Windows系统:
            pip3 install eventlet  # Windows下需要借助eventlet
            # 命令:celery -A 包名 worker -l info -P eventlet
            celery -A celery_task worker -l info -P eventlet


注意版本区别:
# 3.x及以前:
       # celery worker -A 包名 -l info -P eventlet
       celery worker -A celery_task -l info -P eventlet  # 需要切换到指定文件夹
# 4.x及以后:
       # celery -A 包名 worker -l info -P eventlet
       celery -A celery_task worker -l info -P eventlet

4.提交任务:提交异步任务,提交延时任务

可以是项目中的任意位置的文件,只要导入文件没问题 直接右键执行

# submit_task.py

from celery_task.task1 import send_code
from celery_task.task2 import upload

# 提交异步任务
res = send_code.delay('110','995')
print(res)

# 提交延迟任务
from datetime import datetime,timedelta
# 需要获取utc时间,延迟10秒提交
eta = datetime.utcnow() + timedelta(seconds=10)
res1 = upload.apply_async(args=('yessir','清明上河图'),eta=eta)
print(res1)

5.通过id查看执行结果 get_result.py  直接右键执行

# get_result.py

from celery_task.celery import app
from celery.result import AsyncResult

id = '34a48e05-9873-49a4-b190-1d2fbed875ef'  # 根据id获取结果
if __name__ == '__main__':
    asy = AsyncResult(id=id, app=app)
    if asy.successful(): # 顺利执行完了,可以取结果了
        res = asy.get()  # 任务执行的结果
        print(res)
    elif asy.failed():
        print('任务失败')
    elif asy.status == 'PENDING':
        print('任务等待中被执行')
    elif asy.status == 'RETRY':
        print('任务异常后正在重试')
    elif asy.status == 'STARTED':
        print('任务已经开始被执行')

执行定时任务

上面,我们通过 celery 包管理的方式,执行了异步任务和延迟任务,

其实,我们还可以通过 celery 执行定时任务,如下:

第一步: 在 celery.py 中加入配置

# celery.py

from celery import Celery


broker = 'redis://127.0.0.1:6379/1'  # broker任务队列 消息中间件
backend = 'redis://127.0.0.1:6379/2'  # 结构存储,执行完的结果存在这

# 1.实例化得到一个对象app 指定broker消息中间件 和 backend结果仓库
app = Celery(__name__,broker=broker,backend=backend,
             include=['celery_task.task1','celery_task.task2'])


# 执行定时任务
# 时区
app.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
app.conf.enable_utc = False
# 任务的定时配置
from datetime import timedelta
from celery.schedules import crontab
app.conf.beat_schedule = {
    'send_code': {  # send_code为key名
        'task': 'celery_task.task1.send_code',  # 指定task
        # 'schedule': timedelta(seconds=5),  # 每隔5秒
        'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
        'args': ('110', 995),  # 函数传的参数
    }
}


# 执行定时任务其实就是celery.py中多了配置 以及多执行一个自动添加任务的beat服务

第二步:跟方式二一样,写很多任务

第三步:启动worker,启动beat(自动添加任务)  

这两个都需要cd到 celery 包所在的路径(scripts路径下)

# 启动两个任务(worker 和 beat)
cd scripts # 需切换到指定路径 celery -A celery_task worker -l info -P eventlet celery -A celery_task beat -l info # 注意:这里也有版本的区别,详情见方式一/方式二的worker启动

此时,每隔5秒会提交一次任务(或者每周一早上八点会提交任务)

Celery框架在django项目中的使用

详见:利用celery实现异步更新首页轮播图

celery在django项目中使用流程

celery框架django项目工作流程
1)加载django配置环境
2)创建Celery框架对象app,配置broker和backend,得到的app就是worker
3)给worker对应的app添加可处理的任务函数,用include配置给worker的app
4)完成提供的任务的定时配置app.conf.beat_schedule
5)启动celery服务,运行worker,执行任务
6)启动beat服务,运行beat,添加任务
# 重点:由于采用了django的反射机制,使用celery.py所在的celery_task包必须放置项目的根目录下

1.将 celery_task 包放在 项目根路径下(大luffyapi文件夹下)

在 celery_task 包内的 celery.py 文件中配置如下:

# celery.py
from celery import Celery # 加载django环境 import os import django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffyapi.settings.dev") django.setup() broker='redis://127.0.0.1:6379/1' #broker任务队列 backend='redis://127.0.0.1:6379/2' # 结构存储,执行完的结果存在这 app=Celery(__name__,broker=broker,backend=backend,include=['celery_task.home_task',]) # 执行定时任务 # 时区 app.conf.timezone = 'Asia/Shanghai' # 是否使用UTC app.conf.enable_utc = False # 任务的定时配置 from datetime import timedelta from celery.schedules import crontab app.conf.beat_schedule = { 'add-task': { 'task': 'celery_task.home_task.banner_update', 'schedule': timedelta(seconds=60), } }

2.在Terminal终端中:cd到大luffyapi路径下:启动worker,启动beat

在Terminal终端中:
      cd到大luffyapi路径下:启动worker,启动beat

worker:celery -A celery_task worker -l info -P eventlet

beat:celery -A celery_task beat -l info

3.添加定时任务:home_task.py

# home_task.py

from .celery import app  # 导入app

@app.task
def banner_update():
    from home import models
    from home import serializer
    from django.conf import settings
    from django.core.cache import cache
    banner_queryset = models.Banner.objects.filter(
        is_delete=False, is_show=True).order_by('display_order')[:settings.BANNER_DISPLAY]
    banner_serializer = serializer.BannerModelSerializer(instance=banner_queryset,many=True)
    # 通过for循环取值 然后拼接图片完整路径 加载到缓存中
    for banner in banner_serializer.data:
        banner['img'] = 'http://127.0.0.1:8000' + banner['img']
    # 将图片数据存入缓存中
    cache.set('banner_list',banner_serializer.data)
    banner_list = cache.get('banner_list')
    print(banner_list)
    return True

此时,如果每隔1分钟,就会向提交一次定时任务,刷新轮播图的数据信息

 

posted @ 2022-06-10 12:49  _yessir  阅读(172)  评论(0)    收藏  举报