Celery异步分布队列

Celery分布式任务队列

一、Celery介绍celery periodic task

Celery 是一个基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理,如果你的业务场景中需要用到异步任务,就可以考虑使用celery,例:
1.你想对100台机器执行一条批量命令,可能会花很长时间,但不想让程序等着结果返回,而是给你返回一个任务ID,你过一段时间只需要拿着这个任务id就可以拿到任务执行结果,在任务执行ing进行时,你可以继续做其它的事情。
2.你想做一个定时任务,比如每天检测一下你们所有客户的资料,如果发现今天是客户的生日,就给他发个短信祝福

Celery在执行任务时需要通过一个消息中间件和发送任务消息,以及存储任务结果,一般使用rabbitMQ or Redis

Celery有以下优点:

  • 简单:一旦熟悉了celery的工作流程后,配置和使用还是比较简单的
  • 高可用:当任务执行失败或执行过程中发生链接中断,celery会自动尝试重新执行任务
  • 快速:一个单进程的Celery每分钟可处理上百万个任务
  • 灵活:几乎celery的各个组件都可以被扩展及自定制
任务队列是一种跨线程、跨机器工作的一种机制.
任务队列中包含称作任务的工作单元。有专门的工作进程持续不断的监视任务队列,并从中获得新的任务并处理.
celery通过消息(任务)进行通信,通常使用一个叫Broker(中间人)来协助clients(任务的发出者)和worker(任务的处理者). clients发出消息到队列中,broker将队列中的信息派发给worker来处理。

Celery的架构

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

一个celery系统可以包含很多的worker和broker
Celery本身不提供消息队列功能,但是可以很方便地和第三方提供的消息中间件进行集成,包括RabbitMQ,Redis,MongoDB等

二、安装

pip install -U celery==4.4.7 -i  https://pypi.tuna.tsinghua.edu.cn/simple

注意:

Celery不建议在windows系统下使用,因为Celery在4.0版本以后,不再支持windows系统,所以如果要在windows下使用只能安装4.0以前的版本,而且即便是4.0之前的版本,在windows系统下也是不能单独使用的,需要安装eventlet模块

也可从官方直接下载安装包:https://pypi.python.org/pypi/celery/

tar xvfz celery-4.4.7.tar.gz
cd celery-4.4.7
python setup.py build
python setup.py install

三、使用

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

一般celery任务目录直接放在项目的根目录下即可,路径:

luffyapi/
├── mycelery/
    ├── config.py     # 配置文件
    ├── __init__.py   
    ├── main.py       # 主程序
    └── sms/          # 一个目录可以放置多个任务,该目录下存放当前任务执行时需要的模块或依赖
        └── tasks.py  # 任务的文件,名称必须是这个!!!

main.py,代码:

# 主程序
from celery import Celery
# 创建celery实例对象
app = Celery("luffy")

# 通过app对象加载配置
app.config_from_object("mycelery.config")

# 自动搜索并加载任务
# 参数必须必须是一个列表,里面的每一个任务都是任务的路径名称
# app.autodiscover_tasks(["任务1","任务2",....])
app.autodiscover_tasks(["mycelery.sms","mycelery.email"])

# 启动Celery的命令
# 强烈建议切换目录到项目的根目录下启动celery!!
# celery -A mycelery.main worker --loglevel=info

配置文件config.py,代码:

# 任务队列的链接地址
broker_url = 'redis://127.0.0.1:6379/15'
# 结果队列的链接地址
result_backend = 'redis://127.0.0.1:6379/14'

创建一个任务文件sms/tasks.py,并创建任务,代码:

# celery的任务必须写在tasks.py的文件中,别的文件名称不识别!!!
from mycelery.main import app

@app.task  # name表示设置任务的名称,如果不填写,则默认使用函数名做为任务名
def send_sms():
    print("发送短信!!!")

@app.task(name="send_sms2")  # name表示设置任务的名称,如果不填写,则默认使用函数名做为任务名
def send_sms2():
    print("发送短信任务2!!!")

在程序中调用上面的异步任务,拿django进行举例:

# 调用celery执行异步任务
from my_celery.sms.tasks import send_sms
send_sms.delay(mobile)

其他参考文档:

http://docs.celeryproject.org/en/latest/getting-started/introduction.html

https://github.com/celery/celery/tree/master/examples/django/

https://www.jianshu.com/p/1840035cb510

https://flower.readthedocs.io/en/latest/screenshots.html

接下来,我们需要把celery和django组合起来一起使用。

把django和celery进行组合

在main.py主程序中对django的配置文件进行加载

import os,django
from celery import Celery
# 初始化celery对象
app = Celery("luffy")

# 初始化django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.dev')
django.setup()

# 加载配置
app.config_from_object("mycelery.config")
# 自动注册任务
app.autodiscover_tasks(["mycelery.sms","mycelery.email"])
# 运行celery
# 终端下: celery -A mycelery.main worker -l info

在需要使用django配置的任务中,直接加载配置,所以我们把注册的短信发送功能,整合成一个任务函数,mycelery.sms.tasks,代码:

import json,logging
from mycelery.main import app
from ronglian_sms_sdk import SmsSDK
from django.conf import settings
from luffyapi.settings import constants

log = logging.getLogger("django")

@app.task(name="send_sms")
def send_sms(mobile,code):
    """发送短信"""
    sdk = SmsSDK(
        settings.SMS.get("accId"),
        settings.SMS.get("accToken"),
        settings.SMS.get("appId"),
    )

    try:
        resp = sdk.sendMessage(settings.SMS.get("TempId"), mobile, (code, constants.SMS_EXPIRE_TIME // 60))
        resp_data = json.loads(resp)
        if resp_data.get("statusCode") != "000000":
            raise Exception
    except Exception as exc:
        log.error("短信发送失败! 手机号:%s: %s" % (mobile,exc) )
        raise Exception

在这个任务中,我们需要加载短信发送的sdk和相关的配置常量,所以我们可以直接把django中的短信发送模块和相关的常量配置文件直接剪切到当前sms任务目录中

mycelery/
├── config.py
├── __init__.py
├── main.py
└── sms/
    ├── __init__.py
    ├── tasks.py

再次启动项目即可。

最终在django里面,我们调用Celery来异步执行任务。需要完成2个步骤:

# 1. 声明一个和celery一模一样的任务函数,但是我们可以导包来解决
from mycelery.sms.tasks import send_sms

# 2. 调用任务函数,发布任务
send_sms.delay(mobile,code)
# send_sms.delay() 如果调用的任务函数没有参数,则不需要填写任何内容

四、定时任务

定时任务[crontab],主要是依靠:操作系统的定时计划或者第三方软件的定时执行
定时任务的常见场景:
   1. 订单超时
   2. 生日邮件[例如,每天凌晨检查当天有没有用户生日,有则发送一份祝福邮件]
   3. 财务统计[例如,每个月的1号,把当月的订单进行统计,生成一个财务记录,保存到数据库中]
   4. 页面缓存[例如,把首页设置为每隔5分钟生成一次缓存]
   
在django中要实现订单的超时取消,有以下三种方式:
   1. Celery本身提供了定时任务的schedules执行机制
   2. 第三方模块 django-crontab 定时任务
   3. redis值空间回调事件[实际上就是监控字符串的键在过期时,让python自动执行对应的代码段或者方法函数]
      https://blog.csdn.net/qq_42874635/article/details/107314161

Celery官方文档中关于定时任务使用的说明:

http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html

在实现定时任务之前,我们需要先简单使用一下。

我们需要新增一个任务目录,例如order

myceley/
    ├── sms/
    │   ├── __init__.py
    │   └── tasks.py
    ├── config.py
    ├── __init__.py
    ├── main.py
    ├── order/
    │   ├── __init__.py
    │   └── tasks.py
    └── sms

在main.py中,注册任务目录【注意,接下来后面我们使用django的模型处理,所以必须对django的配置进行引入】

import os

from celery import Celery

# 1. 创建示例对象
app = Celery("luffy")

# 2. 加载配置
app.config_from_object("mycelery.config")
# 3. 注册任务[自动搜索并加载任务]
# 参数必须必须是一个列表,里面的每一个任务都是任务的路径名称
# app.autodiscover_tasks(["任务1","任务2"])
app.autodiscover_tasks(["mycelery.sms","mycelery.order"])

# 4. 在终端下面运行celery命令启动celery
# celery -A 主程序 worker --loglevel=info
# celery -A mycelery.main worker --loglevel=info

接下来,在order任务目录下, 创建固定名字的任务文件tasks.py,代码:

from my_celery.main import app

@app.task(name="check_order")
def check_order():
    print("检查订单是否过期!!!")

接下来,我们需要把这个任务设置定时任务,所以需要借助Celery本身提供的Crontab模块。

在配置文件中,对定时任务进行注册:

# 任务队列地址
broker_url = 'redis://127.0.0.1:6379/15'
# 结果队列地址
result_backend = "redis://127.0.0.1:6379/14"
# 设置时区
timezone = 'Asia/Shanghai'

# 使用定时任务必须确保设置了时区
from .main import app
from celery.schedules import crontab
app.conf.beat_schedule = {
    'check-order': {
        'task': 'check_order', # 定时执行的任务
        'schedule': 30.0,      # 定时执行的频率,表示每30秒执行一次
        # 'schedule': crontab(minute="*/5"),  # 定时执行的频率,
        # 'args': (16, 16)     # 定时的任务参数
    },
}

接下来,我们就可以重启Celery并启用Celery的定时任务调度器

先在终端下,运行celery的定时任务程序,以下命令:

celery -A mycelery.main beat  # mycelery.main 是celery的主应用文件

然后再新建一个终端,运行以下命令,上面的命令必须先指定:

celery -A mycelery.main worker --loglevel=info

注意,使用的时候,如果有时区必须先配置好系统时区。

经过上面的测试以后,我们接下来只需改造上面的任务函数,用于判断修改订单是否超时。

要完成订单的任务功能,如果需要调用django框架的模型操作,那么必须针对django框架进行配置加载和初始化。

main.py,代码:

import os,django
from celery import Celery
# 初始化celery对象
app = Celery("luffy")

# 初始化django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.dev')
django.setup()

# 加载配置
app.config_from_object("mycelery.config")
# 自动注册任务
app.autodiscover_tasks(["mycelery.sms","mycelery.email","mycelery.order"])
# 运行celery
# 终端下: celery -A mycelery.main worker -l info

注意,因为在django中是有时区配置的,所以,我们在django框架配置中也要修改时区配置。

任务代码tasks.py的实现:

from mycelery.main import app

@app.task(name="check_order")
def check_order():
    """订单超时取消(业务逻辑)"""
	pass

重新启动celery的定时任务模块和celery的主应用程序。

posted @ 2020-10-15 22:50  HashFlag  阅读(271)  评论(3编辑  收藏  举报