celery+rabbit分布式任务处理

17 篇文章 0 订阅
15 篇文章 1 订阅

前言

celery-分布式任务框架是在rabbbitmq-分布式消息队列之上二次开发的产物。
rabbitmq侧重于处理消息的吞吐,而celery侧重于任务的处理执行。
celery可以在多个服务器上执行任务,但它不能进行消息通讯,需要借助rabbitmq(默认消息处理中间件)或redis来实现任务的分发。
同RabbitMQ一样均为生产者消费者设计模式:①生产者 = => 生成任务,消息②消息队列= =>缓存任务,消息③消费者= =>执行任务,消息

架构

在这里插入图片描述

celery有几个概念:

  • Producer:调用了Celery提供的API、函数或者装饰器而产生任务并交给任务队列处理的都是任务生产者,如上所示即《clien+celery+异步任务》
  • Celery Beat:任务调度器,Beat进程会读取配置文件的内容,周期性地将配置中到期需要执行的任务发送给任务队列,如上《client+celery+定时任务》,区别于Produce的是,Produce执行一次创建一次任务。而Celery Beat执行一次,创建了以按时规则为标准的N次任务
  • Celery Worker:执行任务的消费者,通常会在多台服务器运行多个消费者来提高执行效率,如上《Celery Worker 1 2 3 . N》
  • Result Backend:任务处理完后保存状态信息和结果,以供查询。Celery默认已支持Redis、RabbitMQ、MongoDB、Django ORM、SQLAlchemy等方式,如上《结果存储》

一、关于celery

1、简单:熟悉celery的工作流程之后,配置使用简单
2、高可用:当任务执行失败或执行任务的过程中发生连接中断,celery会自动尝试重新执行任务
3、快速:一个单进程的celery每分钟可处理上百万个任务
4、灵活:celery各个组件几乎都可以被扩展及自定制
5、精准:既可以查看任务的执行情况,又可以对任务编辑(添加|删除|更新)

  • 场景
  • 发送短信/邮件
  • 音视频处理
  • 定时任务

二、使用须知

1、安装celerypip install celery(注意:我安装的是5.0以上的版本,命令跟5.0以下出入有点大)
2、安装RabbitMQ

三、代码

3.1、文件树结构

在这里插入图片描述

3.2、实例文件instance

from celery import Celery

# 创建celery实例
# Celery("celery_demo", broker="redis://127.0.0.1:6379", backend="redis://127.0.0.1:6379/0")
app = Celery("celery_demo")  # celery_demo是Celery实例的名字

# 加载配置文件,绝对路径
app.config_from_object("config")

# 任务注册
"""绝对路径
方式一:导包
import task

方式二:调接口,切记指向包名,函数autodiscover_tasks会自动找包内的task任务文件(任务文件task在package包内,很明显这里没有包,不能使用)
app.autodiscover_tasks(["xxx.task", ])

方式三:Celery对象生成时配置,参数include
app = Celery('celery_demo', include=["task"])

方式四:配置文件config引入,本项目使用
"""

3.3、配置文件config

from datetime import timedelta
from celery.schedules import crontab


# 消息代理使用RabbitMQ。与Celery实例化时broker参数意义相同
broker_url = "amqp://guest:guest@localhost:5672/"

# 结果存储使用redis(默认数据库-零)。与Celery实例化时backend参数意义相同
result_backend = 'redis://127.0.0.1:6379/0'

# Celery打印Terl时LOG输出样式
worker_log_format = "[%(asctime)s] [%(levelname)s] %(message)s"

# Celery指定时区,默认UTC
timezone = "Asia/Shanghai"

# 任务注册到Celery,采用绝对路径。与Celery实例化时
imports = ("task", )

# 定时任务配置,开启进程程序(命令见下文),任务会被按时读取并发送到指定队列
beat_schedule = {
    "add-erery-30-seconds": {
        "task": "task.add",  # 也可以用别名,绝对路径
        "schedule": timedelta(seconds=30),  # 每 30 秒执行一次
        # "schedule": crontab(hour=9, minute=50),  # 每天早上 9 点 50 分执行一次
        "args": (3, 4)
    }
}
#

3.4、任务文件task

import time
import subprocess
from instance import app

# @app.task(queue="#")  # 配置queue之后调用producer可将消息发送至队列(如果RabbitMQ没有该队列名即新建并加入,同时配置的还有exchange与routing_key,值与队列名相同)
# @app.task(name="#")  # 给任务起个别名(默认名为函数名)
@app.task
def add(x, y):
    print("receive parameter")
    print("start sleep")
    time.sleep(5)  # 模拟耗时行为
    # subprocess.Popen("mkdir aa", shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    print("end sleep")
    # 保存x+y结果到数据库redis中,因为已配置结果存储backend
    return x + y

3.5、生产者文件(向队列放异步任务)

from task import add

# 异步任务,不需要等待
add.delay(1, 2)  # delay封装了apply_async方法
print("已加入队列")

3.6、生产者程序(向队列放定时任务)

# 执行后会生成任务调度文件celerybeat-schedule,默认保存到当前位置(可通过"-s path/celerybeat-schedule"指明调度文件的存放位置)
celery --app instance beat

在这里插入图片描述

3.7、消费者Worker

celery worker的工作模式有进程process(常用)、gevent、协程evenlet

3.7.1、进程池

celery -A instance worker -l info --pool=solo -Q celery --concurrency=4

注:

  • A 指向app所在,可用- -app instance代替
  • l 指明celery的日志等级,可用- -loglevel代替
  • Q 指明需要监听的队列名(默认celery),此项目可不填。常用于监听队列名非celery
  • pool 指明并发模型,prefork (默认,multiprocessing),eventlet,gevent,threads,solo(window时使用,否则队列任务消费不了)
  • concurrency 指定并发级别,prefork 模型下就是子进程数量,默认等于 CPU 核心数,根据需要决定是否需要配置
  • worker 指明消费者的意思

四、图解

4.1、消费图解

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

4.2、生产图解

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

4.3、结果图解

在这里插入图片描述

五、路由配置,不常用(config配置文件末尾追加)

由上面代码测试验证之后,了解到,生产者发送消息,会传递到队列celery(没有即新建),于此同时它的交换机exchange名与路由routing_key名均为celery(没有即新建),具体队列信息也可以在消费者Terl运行后看到。
路由配置也就是手动配置队列名、交换机名、路由名以及它们之间的关系、任务task与它们的关系。

需要明确的是:当使用Redis作为Broker时,Exchange的名字必须和Queue的名字一样。任务队列task_queue与任务路由task_routes是一一对应关系(即当task_queue的routing_key不存在时,消息入队列失败;当队列是一个新的queue时,消息将发送到此新队列,而且其属性exchange&routing_key也是新的)

# 默认选项修改
task_default_exchange = 'tasks'  # 将原默认交换机名字celery改为tasks
task_default_exchange_type = 'topic'  # 将原默认的交换机类型direct改为topic
task_default_routing_key = 'task.default'  # 将原默认的路由键celery改为task.default

# 定义任务队列,消费者程序启动时会监听的队列(没有即创建)
task_queues = (
    Queue('default', routing_key='task.#'),  # 路由键以“task.”开头的消息都进default队列
    Queue('web_tasks', routing_key='web.#'),  # 路由键以“web.”开头的消息都进web_tasks队列
)

# 定义任务路由配置,即任务进入哪个队列
task_routes = {
    'task.add': {  # task.add的消息会进入web_tasks队列,绝对路径
        'queue': 'web_tasks',
        'routing_key': 'web.add',
    }
}

描述:
当我生产任务后,生成队列web_tasks(?因为被用到了),当然交换机、路由等属性配置肯定也有了。当我起消费者Worker监听程序后,生成default队列。

六、调试看任务状态

from celery.result import AsyncResult
from instance import app
from task import add


# 向队列添加任务,以此来实现查看任务状态
taskObj = AsyncResult(id=add.delay(2, 3).id, app=app)

# 获取状态码
print(taskObj.status)
# 获取是否消费
print('success', taskObj.successful())
# 获取返回结果,如果任务一直没有消费,一直阻塞等待。。。
print(taskObj.get())

七、django嵌入Celery

celery任务执行需要django代码支撑

7.1、引入django配置(多形式,只讲一种)

import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', app(项目名字).settings')
django.setup()

7.2、配置7.1(必须,否则ERROR)

配置线上
生产任务时引入django配置
消费任务时引入django配置

八、结束!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学无止境gwx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值