15 Celery

Celery

一、简介

1、官方

Celery 官网:http://www.celeryproject.org/

Celery 官方文档英文版:http://docs.celeryproject.org/en/latest/index.html

Celery 官方文档中文版:http://docs.jinkan.org/docs/celery/

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.

2、Celery异步任务框架

"""
1)可以不依赖任何服务器,通过自身命令,启动服务(内部支持socket)
2)celery服务为为其他项目服务提供异步解决任务需求的
注:会有两个服务同时运行,一个是项目服务,一个是celery服务,项目服务将需要异步处理的任务交给celery服务,celery就会在需要时异步完成项目的需求

人是一个独立运行的服务 | 医院也是一个独立运行的服务
	正常情况下,人可以完成所有健康情况的动作,不需要医院的参与;但当人生病时,就会被医院接收,解决人生病问题
	人生病的处理方案交给医院来解决,所有人不生病时,医院独立运行,人生病时,医院就来解决人生病的需求
"""

3、Celery架构

Celery的架构由三部分组成,消息中间件(message broker)、任务执行单元(worker)和 任务执行结果存储(task result store)组成。

img

消息中间件

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

任务执行单元

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

任务结果存储

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

使用场景

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

延迟执行:解决延迟任务

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

Celery的安装配置

pip install celery

消息中间件:RabbitMQ/Redis

app=Celery(‘任务名’, broker=’xxx’, backend=’xxx’)

两种celery任务结构:提倡用包管理,结构更清晰

# 如果 Celery对象:Celery(...) 是放在一个模块下的
# 1)终端切换到该模块所在文件夹位置:scripts
# 2)执行启动worker的命令:celery worker -A 模块名 -l info -P eventlet
# 注:windows系统需要eventlet支持,Linux与MacOS直接执行:celery worker -A 模块名 -l info
# 注:模块名随意


# 如果 Celery对象:Celery(...) 是放在一个包下的
# 1)必须在这个包下建一个celery.py的文件,将Celery(...)产生对象的语句放在该文件中
# 2)执行启动worker的命令:celery worker -A 包名 -l info -P eventlet
# 注:windows系统需要eventlet支持,Linux与MacOS直接执行:celery worker -A 模块名 -l info
# 注:包名随意

二、Celery执行异步任务

1、基本结构

# 创建py文件:celery_app_task.py
import celery
import time
# broker='redis://127.0.0.1:6379/2' 不加密码
backend='redis://:123456@127.0.0.1:6379/1'  # 消息中间件
broker='redis://:123456@127.0.0.1:6379/2'   # 结果存储
app=celery.Celery('test',backend=backend,broker=broker)
@app.task
def add(x,y):
    return x+y

2、包架构封装(多任务结构)

project
    ├── celery_task  	# celery包
    │   ├── __init__.py # 包文件
    │   ├── celery.py   # celery连接和配置相关文件,且名字必须叫celery.py
    │   └── tasks.py    # 所有任务函数
    ├── add_task.py  	# 添加任务
    └── get_result.py   # 获取结果

3、celery快速使用

3.1、main.py
# 第一步:写一个py文件,实例化得到app,编写任务
from celery import Celery

broker = 'redis://127.0.0.1:6379/1'  # 消息中间件
backend = 'redis://127.0.0.1:6379/2'  # 结果存储
app = Celery(__name__, backend=backend, broker=broker)  # app对象的名字


# 写任务(函数),使用装饰器装饰一下
@app.task
def add(a, b):
    import time
    time.sleep(2)
    return a + b
3.2、add_task.py
# 第二步:在其他系统中,提交任务,(导入任务)
from main import add

# 添加立即执行任务
# 异步执行,先提交任务,并不执行
res = add.apply_async(args=[3,4])
print(res)  # e07c0fd8-8542-4efd-b1e2-0a29a0bba7e7
3.3、启动命令
#### 第三步:启动worker执行任务
# 使用命令启动worker
# win机器,需要安装 eventlet
# pip install eventlet
# 3.x 及以前:celery worker -A main -l info -P eventlet
# 4.x及以后:
celery -A main worker -l info -P eventlet

# linux执行下面:
celery -A main worker -l info
必须在包下执行命令
3.4、get_task.py
from main import app
from celery.result import AsyncResult
id = 'ef5c86e0-5efb-4c05-b073-5a872c8a8c28'
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('任务已经开始被执行')
3.5、命令基本使用
1、启动Celery Worker来开始监听并执行任务
celery -A main worker -l info -P eventlet

2、celery beat启动命令
celery -A  celery_task beat -l info

三、包管理形式

1、创建包管理形式

# 第一步:建立如下格式
celery_task  # 包名
    __init__.py
    celery.py  # 必须叫这个名字
    course_task.py # 一堆任务
    home_task.py # 一堆任务
    user_task.py # 一堆任务
add_task.py
get_result.py

2、celery.py

# 第二步,在celery.py中写入
from celery import Celery

broker = 'redis://127.0.0.1:6379/1'  # 消息中间件
backend = 'redis://127.0.0.1:6379/2'  # 结果存储
# include 表示哪些py文件的任务被app管理
app = Celery(__name__, backend=backend, broker=broker,
             include=['celery_task.course_task', 'celery_task.home_task', 'celery_task.user_task'])  # app对象的名字

3、user_task.py

# 第三步:书写任务
from .celery import app   # 使用相对路径导入
import time
@app.task
def send_sms(phone,code):
    print('短信发送成功','手机号为:%s,code为:%s'%(phone,code))
    time.sleep(1)
    return '手机号为:%s,code为:%s'%(phone,code)

4、add_task.py

# 第四步:提交任务
from celery_task.user_task import send_sms

res = send_sms.apply_async(args=['3354325334', 5555])
print(res)  # 146c09eb-be33-426c-a614-53f96f8be563

5、启动woeker命令

# 第五步:启动worker,在包这一层路径
# celery -A 包名 worker -l info -P eventlet
celery -A celery_task worker -l info -P eventlet

6、get_result.py

# 第六步:查看结果

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

id = '146c09eb-be33-426c-a614-53f96f8be563'
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('任务已经开始被执行')

四、执行异步任务和延迟任务

from celery_task import user_task

# 添加立即执行任务
t1 = user_task.add.delay(10, 20)
print(t1.id)


# 添加延迟任务
from datetime import datetime, timedelta
eta=datetime.utcnow() + timedelta(seconds=10)
tasks.low.apply_async(args=(200, 50), eta=eta)

五、执行定时任务

1、 在celery.py中加入配置

#第一步: 在celery.py中加入
# 修改app的配置文件
# 时区
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_sms':{
        'task': 'celery_task.user_task.send_sms',
        'schedule': timedelta(seconds=3),  # 每隔3s
        # 'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
        'args': ('12345667', 8888),
    }
}

####第二步:启动worker和beat

# 启动两个任务(worker 和 beat)
celery -A celery_task beat -l info -P eventlet

2、启动worker 和 beat命令

# 启动worker
celery -A celery_task worker -l info -P eventlet

# 启动beat
celery -A celery_task beat -l info
添加任务:自动添加任务,所以要启动一个添加任务的服务

六、定时更新轮播图缓存

1、home/view.py:定时更新轮播图缓存

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from .models import Banner
from .serializers import BannerSerializer
from django.conf import settings
from django.core.cache import cache
from rest_framework.response import Response


class BannerView(GenericViewSet, ListModelMixin):
    serializer_class = BannerSerializer
    queryset = Banner.objects.all().filter(is_delete=False, is_show=True)[:settings.BANNER_COUNT]

    def list(self, request, *args, **kwargs):
        # 先去缓存中查数据,如果有,之间返回
        res = cache.get('banner_list')
        if res:
            #走了缓存
            print('走了缓存')
            return Response(data=res)
        else:
            print('没走缓存')
            # 如果没有,去数据库查,放入缓存
            response = super().list(request, *args, **kwargs)
            cache.set('banner_list', response.data)
            return response

2、luffy/celery_task/celery.py:配置在根目录下的环境

# 一、加载django配置环境
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffy.settings.dev")

# 二、加载celery配置环境
from celery import Celery

broker = 'redis://127.0.0.1:6379/1'  # 消息中间件
backend = 'redis://127.0.0.1:6379/2'  # 结果存储
# include 表示哪些py文件的任务被app管理
app = Celery(__name__, backend=backend, broker=broker,
             include=['celery_task.course_task', 'celery_task.home_task', 'celery_task.user_task'])  # app对象的名字

# 修改app的配置文件
# 时区
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_sms': {
        'task': 'celery_task.user_task.send_sms',
        'schedule': timedelta(seconds=3),  # 每隔3s
        # 'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
        'args': ('12345667', 8888),
    }
}

3、home_tasks.py

# 添加任务

from .celery import app
from django.core.cache import cache
from home import models, serializers
from django.conf import settings
@app.task
def update_banner_list():
    # 往djagno的缓存中写入轮播图的数据
    queryset = models.Banner.objects.filter(is_delete=False, is_show=True).order_by('-orders')[:settings.BANNER_COUNT]
    banner_list = serializers.BannerSerializer(queryset, many=True).data
    # 拿不到request对象,所以头像的连接base_url要自己组装
    for banner in banner_list:
        banner['image'] = 'http://127.0.0.1:8000%s' % banner['image']

    cache.set('banner_list', banner_list, 86400)
    return True

4、启动worker 和 beat命令

# 启动worker
celery -A celery_task worker -l info -P eventlet

# 启动beat
celery -A celery_task beat -l info
添加任务:自动添加任务,所以要启动一个添加任务的服务
posted @ 2022-02-28 17:01  迷恋~以成伤  阅读(43)  评论(0编辑  收藏  举报