分布式队列celery学习

说明:本文内容来自《python自动化运维快速入门》学习

一、介绍

Celery是由纯Python编写的,但协议可以用任何语言实现。目前,已有Ruby实现的RCelery、Node.js实现的node-celery及一个PHP客户端,语言互通也可以通过using webhooks实现。

1.celery概念

任务队列: 简单来说,任务队列就是存放着任务的队列,客户端将要执行任务的消息放入任务队列中,执行节点worker进程持续监视队列,如果有新的任务,就取出来执行该任务。这种机制就像生产者、消费者模型一样,客户端作为生产者,执行节点worker作为消费者,它们之前通过任务队列进行传递,如图所示。
在这里插入图片描述在这里插入图片描述
中间人(broker): Celery用于消息通信,通常使用中间人(broker)在客户端和worker之前传递,这个过程从客户端向队列添加消息开始,之后中间人把消息派送给worker。官方给出的实现broker的工具可参见下表。

在实际使用中,我们选择RabbitMQ或Redis作为中间人。

在这里插入图片描述
任务生产者: 调用Celery提供的API、函数、装饰器产生任务并交给任务队列的都是任务生产者。
执行单元worker: 属于任务队列的消费者,持续地监控任务队列,当队列中有新的任务时,便取出来执行。
任务结果存储backend: 用来存储worker执行任务的结果,Celery支持不同的方式存储任务的结果,包括AMQP、Redis、memcached、MongoDB、SQLAlchemy等。
任务调度器Beat:Celery Beat进程会读取配置文件的内容,周期性的将配置中到期需要执行的任务发送给任务队列。

2.celery重要特性

高可用性: 如果连接丢失或失败,worker和客户端就会自动重试,并且中间人通过主/主,主/从方式来提高可用性。
快速: 单个Celery进程每分钟执行数以百万计的任务,且保持往返延迟在亚毫秒级(使用RabbitMQ、py-librabbitmq和优化过的设置),可以选择多进程、Eventlet和Gevent三种模式并发执行。
灵活: Celery几乎所有模块都可以扩展或单独使用。可以自制连接池、序列化、压缩模式、日志、调度器、消费者、生产者、自动扩展、中间人传输或更多。
框架集成: Celery易于与Web框架集成,其中的一些甚至已经有了集成包,如django-celery、pyramid_celery、celery-pylons、web2py-celery、tornado-celery。因此,学习Celery具有很强的实用价值。
强大的调度功能: Celery Beat进程来实现强大的调度功能,可以指定任务在若干秒后或指定一个时间点(datetime类)来运行,也可以基于单纯的时间间隔或支持分钟、小时、每周的第几天、每月的第几天以及每年的第几个月的crontab表达式来使用周期任务调度。
易监控: 可以方便地查看定时任务的执行情况,如执行是否成功、当前状态、完成任务花费的时间等,还可以使用功能完备的管理后台或命令行添加、更新、删除任务,提供完善的错误处理机制。

二、启动

django_core目录下启动项目

启动命令

celery -A myCeleryProj.app worker -c 3 -l info

-c 3表示启用三个子进程执行该队列中的任务。运行结果如下:

(venv) PS D:\01-code\django_core> celery -A celery_app.start_celery worker -c 3 -l info
 
 -------------- celery@zhongrf v4.4.0 (cliffs)
--- ***** -----
-- ******* ---- Windows-10-10.0.22000-SP0 2022-12-04 15:28:52
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app:         celery_app:0x1fbf249e048
- ** ---------- .> transport:   redis://127.0.0.1:6379/0
- ** ---------- .> results:     redis://127.0.0.1:6379/0
- *** --- * --- .> concurrency: 3 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
 -------------- [queues]
                .> celery           exchange=celery(direct) key=celery


[tasks]
  . celery_app.tasks.task1.add

[2022-12-04 15:28:52,189: INFO/MainProcess] Connected to redis://127.0.0.1:6379/0
[2022-12-04 15:28:52,189: INFO/MainProcess] mingle: searching for neighbors
[2022-12-04 15:28:52,424: INFO/SpawnPoolWorker-1] child process 63380 calling self.run()
[2022-12-04 15:28:52,439: INFO/SpawnPoolWorker-2] child process 61552 calling self.run()
[2022-12-04 15:28:52,439: INFO/SpawnPoolWorker-3] child process 60512 calling self.run()
[2022-12-04 15:28:53,206: INFO/MainProcess] mingle: all alone
[2022-12-04 15:28:53,206: INFO/MainProcess] celery@zhongrf ready.

更多启动Celery worker的方法如下:

# 设置处理任务队列的子进程个数为10。
celery -A myCeleryProj.app worker -c10 -l info 
# 设置处理任务队列为web_task。
celery -A myCeleryProj.app worker -Q web_task -l info 
# 设置后台运行并指定日志文件位置。
celery -A myCeleryProj.app worker –logfile /tmp/celery.log -l info -D

在window环境,celery4不支持多启动多个worker,需要加上eventlet

celery -A myCeleryProj.app worker -P eventlet -c10 -l info 

调用task的方法有以下三种

1.使用apply_async(args[, kwargs[, …]])发送一个task到任务队列

支持更多的控制,如add.apply_async(countdown=10)表示执行add函数的时间限制最多为10秒;

add.apply_async(countdown=10, expires=120)表示执行add函数的时间限制最多为10秒,add函数的有效期为120秒;

add.apply_async(expires=now + timedelta(days=2))表示执行add函数的有效期为两天。使用apply_async还支持回调,假如任务函数如下:

@app.task
def add(x, y):
return x + y

那么

add.apply_async((2, 2), link=add.s(16))  # 就相当于(2 + 2) + 16 = 20。
2.使用delay(*args, **kwargs)

该方法是apply_async的快捷方式,提供便捷的异步调度,但是如果想要更多的控制,就必须使用方法1。使用delay就像调用普通函数那样,非常简便,如下所示。

task.delay(arg1, arg2, kwarg1='x', kwarg2='y')
3.直接调用,相当于普通的函数调用,不在worker上执行。

三、celery架构

celery完整架构如下:
在这里插入图片描述
任务生产者产生任务并将任务发送到中间人

有多个消费者,即执行单元worker持续地监控消息中间人,如有属于自己队列的任务需要执行,就从中间人那里取出作业名称,查找对应的函数代码并执行,执行完成后将结果存储在Backend。这里的Worker可以分布式部署,彼此之间是独立的。

任务调度器Beat:Celery Beat进程会读取配置文件的内容,周期性地将配置中到期需要执行的任务发送给中间人。

四、Celery 队列

Celery非常容易设置和运行,它通常会使用默认名为Celery的队列(可以通过CELERY_DEFAULT_QUEUE修改)来存放任务。Celery支持同时运行多个队列,还可以使用优先级不同的队列来确保高优先级的任务不需要等待就立即得到响应。

我们来实现不同的队列来执行不同的任务:使任务add在队列default中运行;taskA在队列task_A中运行;taskB在队列task_B中运行。

celery队列配置

celery_app/config/celery_config.py

from kombu import Queue

CELERY_QUEUES = (  # 定义任务队列
    Queue("default", routing_key="task.#"),  # 路由键以“task.”开头的消息都进default队列
    Queue("tasks_A", routing_key="A.#"),  # 路由键以“A.”开头的消息都进tasks_A队列
    Queue("tasks_B", routing_key="B.#"),  # 路由键以“B.”开头的消息都进tasks_B队列
)

CELERY_ROUTES = (
    [
        ("celery_queue_tasks.celery_queue_task.add", {"queue": "default"}),  # 将add任务分配至队列 default
        ("celery_queue_tasks.celery_queue_task.taskA", {"queue": "tasks_A"}),  # 将taskA任务分配至队列 tasks_A
        ("celery_queue_tasks.celery_queue_task.taskB", {"queue": "tasks_B"}),  # 将taskB任务分配至队列 tasks_B
    ],
)
celery 队列启动

开启三个终端窗口,分别启动三个队列的worker,执行以下命令。

celery -A celery_queue_tasks.start_queue_celery worker -Q default -l info
celery -A celery_queue_tasks.start_queue_celery worker -Q tasks_A -l info
celery -A celery_queue_tasks.start_queue_celery worker -Q tasks_B -l info

# 或者一次启动多个队列
 celery -A celery_queue_tasks.start_queue_celery worker -Q default,tasks_A,tasks_B -P eventlet -c 3 -l info

最后开启一个窗口来调用task。

>>> from myCeleryProj.tasks import *
>>> add.delay(4,5);taskA.delay();taskB.delay()

五、Celery Beat任务调度

Celery Beat是Celery的调度器,其定期启动任务,然后由集群中的可用工作节点worker执行这些任务。

默认情况下,Beat进程读取配置文件中CELERYBEAT_SCHEDULE的设置,也可以使用自定义存储,比如将启动任务的规则存储在SQL数据库中。请确保每次只为调度任务运行一个调度程序,否则任务将被重复执行。使用集群的方式意味着调度不需要同步,服务可以在不使用锁的情况下运行。

先明确一个概念——时区。间隔性任务调度默认使用UTC时区,也可以通过时区设置来改变时区。例如:

CELERY_TIMEZONE = 'Asia/Shanghai'  # 通过配置文件设置
app.conf.timezone = 'Asia/Shanghai' #直接在Celery app的源代码中设置

时区的设置必须加入CeleryApp中,默认的调度器(将调度计划存储在celerybeat-schedule文件中)将自动检测时区是否改变,如果时区改变,则自动重置调度计划。其他调度器可能不会自动重置,比如Django数据库调度器就需要手动重置调度计划。

配置

from datetime import timedelta

from celery.schedules import crontab

BROKER_URL = 'redis://127.0.0.1:6379/0'  # 使用redis 作为消息代理

CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/0'  # 任务结果存在Redis

CELERY_RESULT_SERIALIZER = 'json'  # 读取任务结果一般性能要求不高,所以使用了可读性更好的JSON

CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24  # 任务过期时间,不建议直接写86400,应该让这样的magic数字表述更明显

CELERY_TIMEZONE = 'Asia/Shanghai'
CELERYBEAT_SCHEDULE = {
    "add": {
        "task": "celery_beat_tasks.celery_beat_task.add",
        "schedule": timedelta(seconds=10),  # 定义间隔为10s的任务
        "args": (10, 16),
    },
    "taskA": {
        "task": "celery_beat_tasks.celery_beat_task.taskA",
        "schedule": crontab(hour=19, minute=50),  # 定义间隔为对应时区下21:11分执行的任务
    },
    "taskB": {
        "task": "celery_beat_tasks.celery_beat_task.taskB",
        "schedule": crontab(hour=21, minute=8),  # 定义间隔为对应时区下21:8分执行的任务
    },
}

启用

启用Celery Beat进程处理调度任务

celery -A celery_beat_tasks.start_celery_beat beat -l info

最后可以在worker界面看到定时或间隔任务的处理情况

celery -A celery_beat_tasks.start_celery_beat worker -P eventlet -c 3 -l info 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值