【20.0】celery的介绍

【一】celery介绍架构和安装

【1】介绍

celery :分布式的异步任务框架,主要用来做:

  • 异步任务
  • 延迟任务
  • 定时任务---》如果只想做定时任务,可以不使用celery,有别的选择
  • Celery是一个分布式的异步任务框架,可以实现异步任务、定时任务和延迟任务的功能。
  • 在Django中使用Celery,可以更好地处理异步任务,并提高应用的性能和可扩展性。

(1)安装Celery和Redis

  • 首先,在Django项目中安装Celery和Redis:
pip install celery redis

(2)配置Celery

  • 在Django项目的settings.py文件中,添加Celery的配置信息:
# settings.py

CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
  • 上述配置中,CELERY_BROKER_URL指定了Celery的消息代理(这里使用Redis),CELERY_RESULT_BACKEND指定了Celery任务结果的存储位置。

(3)创建celery.py文件

  • 在Django项目的根目录下创建一个名为celery.py的文件,并添加以下内容:
# celery.py

import os
from celery import Celery

# 设置Django环境变量
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project.settings')

# 创建Celery实例
app = Celery('your_project')

# 加载Celery配置
app.config_from_object('django.conf:settings', namespace='CELERY')

# 自动发现异步任务
app.autodiscover_tasks()

(4)定义异步任务

  • 在Django项目的某个应用中,创建tasks.py文件,并定义异步任务:
# your_app/tasks.py

from celery import shared_task

@shared_task
def send_email_task(to, subject, message):
    # 模拟发送邮件的耗时操作
    print(f"Sending email to {to}: {subject}")
    print(f"Message: {message}")
  • 在上面的代码中,使用shared_task装饰器定义一个名为send_email_task的异步任务。

(5)调用异步任务

  • 在Django的视图函数或其他逻辑中,可以通过调用异步任务来实现对应的功能:
# views.py

from your_app.tasks import send_email_task

def some_view(request):
    # 异步调用发送邮件任务
    send_email_task.delay("example@example.com", "Hello", "This is a test email")
    return HttpResponse("Task started")
  • 在上述代码中,通过send_email_task.delay()方法异步调用发送邮件任务,并立即返回响应给用户,不会阻塞主线程。

(6)注意事项:

  • 为了确保Celery能够自动发现异步任务,需要在Django项目根目录下运行Celery的worker进程:
    celery -A your_project worker --loglevel=info
    
  • 如果需要使用定时任务和延迟任务,在Celery配置中添加相应的定时器和调度器,例如使用beat_schedule配置项。

(7)总结:

  • 在Django中使用Celery可以实现异步任务、定时任务和延迟任务的功能,提高应用的性能和可扩展性。
  • 需要安装Celery和消息代理(如Redis)。
  • 配置Celery信息在Django的settings.py文件中。
  • 创建celery.py文件来加载Celery配置并自动发现异步任务。
  • 定义异步任务并使用shared_task装饰器进行注册。
  • 在适当的地方调用异步任务并使用.delay()方法来异步执行任务。

【2】celery 框架,原理

  • 1)可以不依赖任何服务器,通过自身命令,启动服务(内部支持socket)
    • Celery可以独立地启动服务,无需依赖其他服务器,通过自身命令来启动服务。
    • 它内部支持socket通信。
  • 2)celery服务为为其他项目服务提供异步解决任务需求的
    • Celery服务提供解决异步任务需求的功能,为其他项目服务。
    • 在使用Celery的情况下,会同时存在两个服务运行:项目服务和Celery服务。
    • 项目服务将需要异步处理的任务交给Celery服务,然后Celery服务会在需要时以异步方式完成项目的需求。
  • 注:会有两个服务同时运行,一个是项目服务,一个是celery服务,项目服务将需要异步处理的任务交给celery服务,celery就会在需要时异步完成项目的需求
  • 人是一个独立运行的服务 | 医院也是一个独立运行的服务
  • 正常情况下,人可以完成所有健康情况的动作,不需要医院的参与;但当人生病时,就会被医院接收,解决人生病问题
  • 人生病的处理方案交给医院来解决,所有人不生病时,医院独立运行,人生病时,医院就来解决人生病的需求

【3】celery架构

  • 消息中间件(broker):消息队列:可以使用redis,rabbitmq,咱们使用redis
  • 任务执行单元(worker):真正的执行 提交的任务
  • 任务执行结果存储(banckend):可以使用mysql,redis,咱们使用redis

(1)消息中间件(Broker):

  • 消息队列被用作任务的传输通道,它负责将任务请求发送给任务执行单元。

  • 在Celery中,我们可以选择使用不同的消息中间件,比如Redis、RabbitMQ等。在这里,我们选择使用Redis作为消息中间件。

(2)任务执行单元(Worker):

  • 任务执行单元是真正执行任务的组件。

  • 它会监听消息中间件,接收到任务请求后,根据配置的调度策略,将任务交给相应的Worker来执行。

  • Worker可以运行在一个或多个节点上,并行地处理多个任务。

  • 每个Worker都会独立处理任务并报告执行结果。任务执行单元是Celery的核心功能模块。

(3)任务执行结果存储(Backend):

  • 任务执行结果存储组件用于保存任务执行的结果信息,方便后续查询和处理。
  • Celery允许使用不同的后端存储,比如MySQL、Redis等。在这里,我们选择使用Redis作为任务执行结果的存储。

(4)总结

  • Celery的架构包括消息中间件(Broker)、任务执行单元(Worker)和任务执行结果存储(Backend)。
  • 消息中间件负责接收任务请求并将其分发给相应的Worker进行执行,任务执行结果存储组件则用于保存任务的执行结果。
  • 这个架构能够高效地完成任务的分发、执行和结果存储。

(5)图解

  • 任务(tasks):用户定义的函数,用于实现用户的功能,比如执行一个耗时很长的任务。
  • 中间介(Broker):用于存放tasks的地方,但是这个中间介需要解决一个问题,就是可能需要存放很多tasks,而且要保证Worker能够从这里拿取任务。
  • 执行者(Worker):用于执行tasks,也就是真正调用我们在tasks中定义的函数。
  • 储存(backend):把执行tasks返回的结果进行存储,以供用户查看或者调用

【二】安装celery

【1】使用pip命令进行安装:

pip install Celery

【2】安装完成后,需要将可执行文件celery添加到系统环境变量中

【3】注意:

  • celery不支持win,所以想再win上运行,需要额外安装eventlet
  • windows系统需要eventlet支持:pip3 install eventlet
  • Linux与MacOS直接执行:
    • 3.x,4.x版本:celery worker -A demo -l info
    • 5.x版本: celery -A demo worker -l info -P eventlet

(1)Windows

  • 如果你使用的是Windows系统,需要额外安装eventlet,因为celery不支持Windows。
  • 可以使用以下命令安装eventlet
pip3 install eventlet

(2)Linux/MacOS

  • 对于Linux和MacOS系统,可以直接通过以下命令运行celery

  • 对于3.x和4.x版本:

celery worker -A demo -l info
  • 对于5.x版本:
celery -A demo worker -l info -P eventlet

celery -A celery worker -l info -P eventlet
  • 以上是安装和配置celery的详细步骤。请确保按照上述格式进行操作,并根据你所使用的系统选择正确的命令。

【三】使用

【1】简单使用

  • 虚拟环境中装celery和eventlet
  • 写个py文件,实例化得到app对象,注册任务
from celery import Celery
import time

# 结果存储在Redis
# 如果有密码 : redis:密码//127.0.0.1:6379/0
backend = 'redis://127.0.0.1:6379/0'
# 消息中间件
broker = 'redis://127.0.0.1:6379/1'

# 实例得到APP
app = Celery('tasks', backend=backend, broker=broker)

# 给需要的任务添加装饰器
@app.task
def add(x, y):
    time.sleep(2)
    print('add运算结果:>>', x + y)
    return x + y
# 导入任务模块
from celery_start import add

# 同步运行
res = add(4, 5)
print(res)  # 9

# 异步调用
# 执行下面,实际上是:把任务提交到消息队列中,返回一个ID号,后期通过ID号查询任务执行结果,并没有执行,需要等待worker执行
res1 = add.delay(6, 6)
print(res1)  # c1502fc4-f787-40b1-9bd1-4e8b830b551b

【2】启动worker

  • worker监听消息队列,等待别人提交任务,如果没有就卡再这
celery -A celery_start worker -l info -P eventlet
  • 执行异步调用
    • 在redis中就会看到相应的结果
{
  "status": "SUCCESS",
  "result": 12,
  "traceback": null,
  "children": [

  ],
  "date_done": "2023-08-11T01:36:19.732731",
  "task_id": "c1502fc4-f787-40b1-9bd1-4e8b830b551b"
}

【3】提交任务返回ID号

  • 别人 提交任务,提交完成会返回一个id号,后期使用id号查询
    • 至于这个任务有没有被执行,取决于worker有没有启动
from celery_start import add
res=add.delay(77,66)

【4】查看任务结果

  • 此时的ID号就是上面已经在redis中存过的执行任务的ID号
from celery_start import app
# celery的包下
from celery.result import AsyncResult

id = 'c1502fc4-f787-40b1-9bd1-4e8b830b551b'
if __name__ == '__main__':
    a = AsyncResult(id=id, app=app)
    if a.successful():  # 正常执行完成
        result = a.get()  # 任务返回的结果
        print(result)
    elif a.failed():
        print('任务失败')
    elif a.status == 'PENDING':
        print('任务等待中被执行')
    elif a.status == 'RETRY':
        print('任务异常后正在重试')
    elif a.status == 'STARTED':
        print('任务已经开始被执行')
  • 结果
12

【四】celery执行异步任务

(1)新建包:celery_task

(2)新建文件:celery.py

  • 必须叫celery.py,里面实例化得到app对象
from celery import Celery

# 消息中间件
broker = "redis://127.0.0.1:6379/1"
# 存储结果
backend = "redis://127.0.0.1:6379/0"
# 存放需要处理任务的列表
include = ['celery_task.user_task']
# 实例化得到celery对象
app = Celery(__name__, broker=broker, backend=backend, include=include)

(3)新建任务py文件

  • 例如user_task.py/course_task.py

  • celery_task/user_task.py

from .celery import app
import time
@app.task
def send_sms(mobile, code):
    time.sleep(2)
    print('%s手机号,发送短信成功,验证码是:%s' % (mobile, code))
    return True

(4)启动worker

  • 以包启动,来到包所在路径下
    • 特别提醒:在我们建的包的上一层文件夹下执行命令
celery -A 包名 worker -l info -P eventlet
celery -A celery_task worker -l info -P eventlet

(5)提交异步任务(.delay)

  • 其它程序,导入任务,提交任务即可
from celery_task.user_task import send_sms
res = send_sms.delay(1999999333, 8888)
print(res)  # f33ba3c5-9b78-467a-94d6-17b9074e8533

(6)查询结果

  • 其它程序,查询结果
from celery_task.celery import app
# celery的包下
from celery.result import AsyncResult


def get_result(task_id):
    back_dict = {"code": 200, "result": "处理完成"}
    result = AsyncResult(id=task_id, app=app)
    if result.successful():  # 正常执行完成
        result = result.get()  # 任务返回的结果
        back_dict['result'] = result
        return back_dict
    elif result.failed():
        back_dict['code'] = '500'
        back_dict['result'] = '任务失败'
        return back_dict
    elif result.status == 'PENDING':
        back_dict['code'] = '201'
        back_dict['result'] = '任务等待被执行'
        return back_dict
    elif result.status == 'RETRY':
        back_dict['code'] = '301'
        back_dict['result'] = '任务异常,正在重试'
        return back_dict
    elif result.status == 'STARTED':
        back_dict['code'] = '202'
        back_dict['result'] = '任务已开始'
        return back_dict


if __name__ == '__main__':
    id = '51a669a3-c96c-4f8c-a5fc-e1b8e2189ed0'
    get_result(id)

【五】celery执行延迟任务

from datetime import datetime, timedelta
from celery_task.user_task import send_sms


print(datetime.now())  # 2023-08-11 11:20:53.008287
# 延迟任务使用的是utc时间,没有做时间国际化
print(type(datetime.utcnow()))  # <class 'datetime.datetime'>
print(timedelta(minutes=3, seconds=2))  # 0:03:02
# 可以和 datetime.datetime 相加减
print(type(timedelta(hours=1)))  # <class 'datetime.timedelta'>

# 延迟任务,延迟 5s 后,再发送短信
# 参数1:任务函数, send_sms
# 参数2:任务函数参数, args=[1999999333, 8888]
# 参数3:延迟时间,单位是秒,所以这里延迟 5s ,countdown=5
# 参数4:eta, 任务执行时间
eta = datetime.utcnow() + timedelta(seconds=10)
# 任务运行即被提交,但是5s会才会被处理
send_sms.apply_async(args=[1999999333, 8888], countdown=5, eta=eta)

【六】celery执行定时任务

【1】修改启动配置文件

  • celery_task\celery.py
from datetime import timedelta
from celery import Celery
from celery.schedules import crontab

# 消息中间件
broker = "redis://127.0.0.1:6379/0"
# 存储结果
backend = "redis://127.0.0.1:6379/1"
# 存放需要处理任务的列表
include = ['celery_task.user_task']
# 实例化得到celery对象
app = Celery(__name__, backend=backend, broker=broker, include=include)

# APP 配置
app.conf.beat_schedule = {
    'send_sms': {
        # 执行的任务函数
        'task': 'celery_task.user_task.send_sms',
        # 延迟时间
        # 'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
        # 'schedule': crontab(hour=11, minute=35),  # 每天11点35,执行
        'schedule': timedelta(seconds=5),
        'args': ('1822344343', 8888),
    },
}

【2】 启动beat+启动worker

  • 启动beta
celery  -A 包名 beat -l info 

celery  -A celery_task beat -l info 
  • 启动worker
celery -A 包名 worker -l info -P eventlet

celery -A celery_task worker -l info -P eventlet

【3】任务执行

  • 到了指定时间,beat进程负责提交任务到消息队列
    • worker执行

【七】Django中使用celery

  • celery中要使用djagno的东西,才要加这句话
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffy_api.settings.dev")

【1】将做好的包copy到项目根目录下

【2】在包下写任务

  • luffyCity\celery_task\home_task.py
from luffyCity.apps.home.models import Banner
from .celery import app


@app.task
def add_banner():
    banner = Banner.objects.create(title='测试', image='1.png', link='/about', info="测试", orders=99)
    return "增加成功"

【3】在celery.py 中加载django配置

  • luffyCity\celery_task\celery.py
import os
from datetime import timedelta
from celery import Celery
from celery.schedules import crontab

# celery 使用 Django 需要 注册Django
# Django配置文件的路径
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffyCity.settings.dev")

# 消息中间件
broker = "redis://127.0.0.1:6379/0"
# 存储结果
backend = "redis://127.0.0.1:6379/1"
# 存放需要处理任务的列表
include = ['celery_task.user_task']
# 实例化得到celery对象
app = Celery(__name__, backend=backend, broker=broker, include=include)

# APP 配置 +---+ 定时任务配置
# app.conf.beat_schedule = {
#     'send_sms': {
#         # 执行的任务函数
#         'task': 'celery_task.user_task.send_sms',
#         # 延迟时间
#         # 'schedule': crontab(hour=8, day_of_week=1),  # 每周一早八点
#         # 'schedule': crontab(hour=11, minute=35),  # 每天11点35,执行
#         'schedule': timedelta(seconds=5),
#         'args': ('1822344343', 8888),
#     },
# }

【4】视图函数中执行任务

  • luffyCity\luffyCity\apps\user\views.py
# 测试celery
# 前端发送请求,异步向banner表中插入记录
from celery_task.home_task import add_banner
class CeleryTestResponse(APIView):
    def get(self, request):
        '''
        # 同步使用任务
        from celery_task.home_task import add_banner
        add_banner()
        '''
        # 异步使用
        res = add_banner.delay()
        print(res)

        return CommonResponse(msg='celery测试成功')

【5】启动worker

  • 执行命令
celery -A celery_task worker -l info -P eventlet
  • 注意执行命令的路径位置
(venv) PS E:\Old Boy\luffy\luffyCity> celery -A celery_task worker -l info -P eventlet

【补充】异步处理短信/秒杀场景

(1)异步处理短信

(2)处理秒杀场景

posted @ 2023-08-19 17:14  Chimengmeng  阅读(37)  评论(0编辑  收藏  举报