Celery 介绍

17 阅读5分钟

1、Celery 介绍

Celery 大致有两种应用场景:异步任务定时任务

1. 异步任务

假设在一个接口请求中,某个任务的执行时间非常长,而前端页面并不需要立刻获取处理结果。在这种情况下,可以将这个任务作为 异步任务,先返回给前端一个“处理中”的信息,然后在后台单独运行这个任务,任务执行完成后再返回结果。

2. 定时任务

例如某个任务需要每天晚上运行一次,而我们不可能每天都手动执行这个任务。可以使用 Celery 来实现这个定时任务的周期性执行,例如设置每天晚上十点执行某个函数。

Celery 的组成

Celery 主要由以下组件组成:

1. Task (任务)

Task 是指异步任务或定时任务,可以通过 Celery 定义和管理。这些任务可以被发送到 Broker 进行处理。

2. Broker (消息中间件)

Broker 可以理解为消息中间件,它的作用是获取异步任务或定时任务,并将这些任务加入消息队列中。然后,消息队列将任务发送给 Worker 进行处理。

  • 常见的 Broker 实现有:RedisRabbitMQ 或者其他消息中间件。
  • 在这个例子中,我们使用 Redis 作为消息中间件。

3. Worker (工作进程)

Worker 是实际执行任务的程序。它从 Broker 中获取任务,并在 Worker 中执行相应的操作。执行完成后,根据配置,结果会被发送到 Backend 进行存储。

4. Result Backend (结果存储)

当任务执行完毕后,Worker 会将任务结果存储到 Result Backend 中。无论任务有无返回结果,都会根据配置将结果发送到指定的存储位置。常见的 Result Backend 有:

  • Redis
  • 数据库(如 PostgreSQL、MySQL)
  • 文件系统等

5. Beat (定时任务调度器)

Beat 主要用于定时任务的调度。根据预定的时间规则(如每天晚上十点),它会将 Task 发送给 Broker,然后 Worker 获取并执行任务。定时任务可以分为两类:

  • 周期任务:例如每天固定时间执行某个任务。
  • 间隔任务:例如每隔一定时间(如每 10 分钟)执行一次任务。

6. 异步任务 vs 定时任务

  • 异步任务:异步任务的发送不经过 Beat,直接发送到 Broker,然后由 Worker 执行。
  • 定时任务:定时任务则由 Celery Beat 调度,依照预定时间发送给 Broker,由 Worker 执行。

配置与运行

在 Celery 中,Broker(例如 Redis)需要启动并运行,以便 BeatWorker 能够访问和处理任务。

  • WorkerBeat 都需要通过手动启动程序来运行。
  • 每次修改定时任务的配置后,需要重新启动 BeatWorker 才能使更改生效。

典型流程

  1. 定义任务(Task),并发送给 Broker
  2. Beat 根据设定的定时任务规则(周期性或间隔性)触发任务,将任务发送到 Broker
  3. WorkerBroker 中获取任务并执行。
  4. 执行结果根据配置存储到 Result Backend

注意事项

  • 异步任务不经过 Beat,直接由 Worker 执行。
  • 定时任务则由 Celery Beat 调度,确保任务按照预定时间触发。
  • 更改定时任务配置后,需要重新启动 BeatWorker 才能使更改生效。

2、Celery 准备

接下来,我们将实现一个最简单的异步任务,在执行异步任务之前,我们需要做以下准备工作:

1. 安装依赖

我们需要安装 Celery 和 Redis 的依赖:

pip3 install celery -i https://mirrors.aliyun.com/pypi/simple/
pip3 install redis -i https://mirrors.aliyun.com/pypi/simple/
2. 消息中间件 这里我们使用的消息中间件是 Redis,可以去官网下载安装 Redis,也可以使用 Docker 来执行安装:
docker run -itd -p 6379:6379 redis:latest
3. 异步任务准备 准备一个最简单的 add() 函数,放在 tasks.py 文件中:
# tasks.py
from celery import Celery

app = Celery('tasks', broker='redis://localhost/0', backend='redis://localhost/0')

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

在这段代码里,我们引入了 Celery 模块,并将其实例化为 app,且设置了 broker 参数,表示消息队列的第二个数据源是 Redis。

在这段代码里,我们引入 Celery 模块,并将其实例化为 app,且配置了 broker 参数,表示消息队列将被放在 Redis 的第一个数据库下。

指定的 backend 参数则表示函数运行的结果被存储在 Redis 的第二个数据库下。

然后用 @app.task 修饰 add 函数,表示它是 app 下的任务(task)。

以上,我们的准备工作就完成了,接下来尝试执行这个异步任务。

3、 Celery 启动和异步任务的运行

说是 Celery 的启动,其实是 Worker 的启动,中间件是 Redis,已经在前面的步骤中启动了。

我们在 tasks.py 所在的文件夹下执行下面的命令:

celery -A tasks worker -l INFO

在这里,tasks 是我们任务所在的文件名,worker 表示启动的是 Worker 程序。

-INFO 则会在控制台打印出 Worker 接收到的消息详情,如果不执行该参数,则信息流不会被打印出来。

执行了上面的程序后,可以看到控制台会输出下面这种信息:

-------------- celery@localhost v5.1.2 (sun-harmonics)
--- ***** ----
-- ******** -- Darwin-21.4.0-x86_64-i386-64bit 2022-07-17 23:56:09
--*** --- * ---
- ** -------- [config]
- ** ---------- -> app: tasks:0x7f8c8ddf3d98
- ** ---------- -> transport: redis://localhost:6379/0
- ** ---------- -> results: disabled://
- ** --- * ---- -> concurrency: 12 (prefork)
 - ** --------- -> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** ----
-------------- [queues]
  - -> celery exchange=celery(direct) key=celery

[tasks]
  . tasks.add

[2022-07-17 23:56:09,685: INFO/MainProcess] Connected to redis://localhost:6379/0
[2022-07-17 23:56:09,699: INFO/MainProcess] mingle: searching for neighbors
[2022-07-17 23:56:10,731: INFO/MainProcess] mingle: all alone
[2022-07-17 23:56:10,737: INFO/MainProcess] celery@localhost ready.