5

celery笔记一之celery介绍、启动和运行结果跟踪 - XHunter

 1 year ago
source link: https://www.cnblogs.com/hunterxiong/p/17450464.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
1.celery笔记一之celery介绍、启动和运行结果跟踪06-01

本文首发于公众号:Hunter后端
原文链接:celery笔记一之celery介绍、启动和运行结果跟踪

本篇笔记内容如下:

  1. celery 介绍
  2. celery 准备
  3. celery 启动和异步任务的运行
  4. 运行结果跟踪

1、celery 介绍

celery 大致有两种应用场景,一种是异步任务,一种是定时任务。

比如说在一个接口请求中,某个函数执行所需的时间过长,而前端页面并不是立刻需要在接口中获取处理结果,可以将这个函数作为异步任务,先返回给前端处理中的信息,在后台单独运行这个函数,这就是异步任务。

另一个比如说某个函数需要每天晚上运行一遍,不可能人天天守着后台手动执行一遍这个函数,那么就可以用 celery 来实现这个定时的周期任务。

接下来介绍一下 celery 的组成:

task

这个任务就是我们前面举的例子的异步任务或者是定时任务,即为 task,我们可以定义这些任务,然后发送到 broker

broker

broker 可以理解成消息中间件,用于获取异步或者定时任务,形成一个或多个消息队列,然后发送给 worker 处理这些消息

broker 的形式可以是 Redis,RabbitMQ 或者其他,这里我们使用 Redis 作为消息中间件

worker

worker 是处理消息的程序,获取 broker 中的消息,然后在 worker 中执行,然后根据配置决定将处理结果发送到 backend

result_backend

在 worker 处理完消息之后会有 return 或者没有返回结果,都会根据配置将结果发送出来,可以配置成发送到 redis 中,也可以将之存储到 database 中

beat

主要用于调用定时任务,根据设定好的定时任务,比如每天晚上十点执行某个函数,beat 则会在相应的时间将这个 task 发送给 broker,然后 worker 获取任务进行处理

定时任务除了说的每天晚上十点这种周期任务,也可以是间隔任务,比如说每隔多少秒,多少分钟执行一次

注意:异步任务的发送是不经过 beat 处理,直接发送给 broker 的

在上面的结构中,broker 需要将相应的服务比如 redis 运行起来,而 worker 和 beat 需要在手动用程序运行,而且每次更改了定时策略之后需要重新启动 beat 和 worker 才能生效。

2、celery 准备

接下来我们实现一个最简单的异步任务,在执行异步任务前,我们做如下的准备工作

1.安装依赖

我们需要安装一下 celery 和 redis 的依赖:

pip3 install celery==5.1.2 -i https://mirrors.aliyun.com/pypi/simple/
pip3 install redis==3.5.3 -i https://mirrors.aliyun.com/pypi/simple/

2.消息中间件

这里我们用到的消息中间件是 redis,可以去官网下载一个 redis 启动,也可以使用 docker 来执行安装。

我在之前的 docker 系列笔记中有介绍过如何拉取镜像和运行容器,我们这里直接使用 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/1')

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

在这段代码里,我们引入 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 程序

-l 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:0x7fc8ddf3df98
- ** ---------- .> 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,737: INFO/MainProcess] mingle: all alone
[2022-07-17 23:56:10,780: INFO/MainProcess] celery@localhost ready.

则表示 worker 启动成功

执行异步任务

在另一个 shell 窗口,进入 python 的交互界面,输入以下命令:

from tasks import add
res = add.delay(1,2)

add 是我们需要执行的异步任务的函数名

delay 是异步任务执行的特有方法,这个其实是 apply_async() 函数的简便写法,不带任何参数,apply_async() 除了可以实现异步任务的功能,还可以指定多少时间后执行,比如说二十秒后执行,这个在后面的笔记中我们再介绍。

而异步任务的返回我们这里用 res 来定义,它是一个包含了这个任务所有执行信息对象,有任务状态(是否执行成功),有返回结果(add() 函数的return),还有这个 task 特有的标识 id等信息

至此,我们的一个异步任务的执行就完成了,我们可以在下一步查看它的运行结果等信息。

4、运行结果跟踪

接下来,我们在 tasks.py 中建立下面几个函数,来测试我们对结果的跟踪:

# tasks.py

import time
from celery import Celery

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


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


@app.task
def div(x, y):
    return x / y


@app.task
def test_not_finished():
    time.sleep(30)
    return True

然后重新运行 worker:

celery -A tasks worker -l INFO

然后引入和执行函数:

from tasks import add, div, test_not_finished

获取延时任务的结果

res = add.delay(1, 2)
print(res.result)

# 也可以使用 get() 
print(res.get())

get() 函数也可以加个超时的设置:

res.get(timeout=2)

但是这样需要注意,因为如果超时了还未获取到结果,程序就会报错

判断函数运行是否完成

print(res.ready())

打印出的结果为 True 则表示函数运行完成

我们可以测试函数为完成的状态:

res2 = test_not_finished.delay()

在这个函数里,我们设置了 30s 的休眠,所以在 30s 內我们打印结果可以看到 res2.ready() 是为 False 的:

print(res2.ready())

获取task id

每个被执行的 task 都有各自对应的 id 作为它们的唯一键:

print(res.id)

查看任务执行的状态

# 任务执行是否失败,返回 布尔型数据
is_failed = res.failed()

# 任务执行是否成功,返回布尔型数据
is_successful = res.successful()

# 执行的任务所处的状态
state = res.state
# state 的值会在 PENDING,STARTED,SUCCESS,RETRY,FAILURE 这几种状态中,分别是 待处理中,任务已经开始,成功,重试中,失败

报错处理

如果执行的延时任务在程序中报错,比如我们定义的 div() 函数,我们传入的除数为 0 的话,在程序中是会报错的,我们使用 get() 来获取结果的话程序是会报错的:

res3 = div.delay(3, 0)
res3.get()

# 返回会报错

但是我们可以使用 propagate=False 参数来忽略程序的报错:

res3.get(propagate=False)

这样我们获取的就不是程序报错,而是程序报错的信息作为结果返回

使用 res3.state 发现返回的结果是 FAILURE

当延时任务在程序中报错,它的返回值就不会是正确的,我们可以通过 res3.traceback 是否有值来判断函数运行过程中是有报错:

if res3.traceback:
    print("延时任务报错")
else:
    print("程序正常运行,可以获取返回值")

result资源释放

因为 backend 会使用资源来保存和传输结果,为了确保资源被释放,所以在执行完异步任务后,你必须对每一个结果调用 get() 或者 forget() 函数

result.get() 函数获取结果

result.forget() 在 backend 删掉该数据

在官方文档上,意思是 get() 和 forget() 方法都可以释放资源,但是经过我测试,貌似只有 forget() 函数会释放资源

查看是否资源被释放也很简单,登录到对应的 backend,我这里是 redis,使用 redis-cli 或者通过 docker 进入 redis:

select 1

keys*

查看相应的 task id 是否还在列表就可以知道该资源是否被释放

如果不想手动释放资源,可以在配置里设置一个过期时间,那么结果就会在指定时间段后被释放:

app.conf.update(result_expires=60)

这个我们可以在后面的配置里再详细介绍。

以上就是本篇笔记全部内容,下一篇笔记我们将介绍如何建立一个 celery 项目、配置的几种方法及一些基本的配置。

如果想获取更多后端相关文章,可扫码关注阅读:

image

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK