Celery使用小结

介绍

Celery 是异步任务调度工具就,或分布式任务队列(Distributed Task Queue),有多个worker。

Broker:中间人,所有任务放在broker中,worker到broker中提取任务执行。

这样系统为开环系统,broker无法知道任务执行情况,可使用backend broker获取任务执行结果,

broker:消息传输的中间件,即消息队列,发送和接收结果;

backend:数据库,存储消息接celery执行的消息及结果;可以是database 或cached。

celery 组成:消息中间件、任务执行单元、执行结果存储。

celery不提供消息服务,但可与其他消息中间件集成;

import time
from urllib.parse import quote
from celery import Celery

broker_url = "redis://:{}@{}:{}".format(quote("#123"), redis_ip, "6379/0")  # 消息中间件
result_backend = "redis://:{}@{}:{}".format(quote("#123"), redis_ip, "6379/1")  # 存储结果
app = Celery('my_tasks', broker=broker_url, backend=result_backend)

@app.task
def add(x, y):
    time.sleep(1)
    return x + y
@app.task
def div(x, y):
    return x / y

启动 celery,上述代码放在名为 tasks.py 的文件中,命令行进入tasks.py目录,

Linux环境执行命令:celery -A soft_time_limit_demo worker --loglevel=info

运行命令:celery -a tasks -P gevent --loglevel=info;启动后,运行如下命令,celery就会收到任务并给出结果。

res = add.delay(1, 2)
# time.sleep(3)  # 给任务执行时间
print(f"tasks finished 1: {res.ready()}")
print(res.result)  # None, 任务未执行完
print(f"tasks finished 2: {res.ready()}")
print(res.get())
print(f"tasks finished 3: {res.ready()}")
try:
    print(res.get(timeout=2))  # 未检测到超时
except Exception as e:
    print(f"Exception: {e}")

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

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

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

res3 = div.delay(3, 0)
try:
    res3.get()  # 返回会报错
    # 使用参数来忽略程序的报错: propagate = False
    r = res3.get(propagate=False)  # 程序报错信息作为结果返回, 使用 res3.state 的结果是 FAILURE
    print(r)
except Exception as e:
    print(f"Exception 3: {e}")

# 当延时任务在程序中报错,它的返回值就不会是正确的,可以通过 res3.traceback 是否有值来判断函数运行过程中是有报错:
res4 = div.delay(4, 0)
print(res4.traceback)  # None
if res4.traceback:
    print("延时任务报错")
else:
    print("程序正常运行,可以获取返回值")

注意:在Windows下,不指定 -P gevent,celery收到任务但不执行,即不会有任务执行结果。gevent并行库有一个替代的方案是eventlet,也可以 -P eventlet,需要pip install eventlet。

delay 是异步任务执行的特有方法,是 apply_async() 的简写,不带任何参数,apply_async() 除了可以实现异步任务的功能,还可以指定多少时间后执行,比如说二十秒后执行。异步任务的返回用 res 定义,包含了这个任务所有执行信息对象,有任务状态(是否执行成功),有返回结果(add() 函数的return),还有这个 task 特有的标识 id 等信息。

backend 会保存和传输结果,在执行完异步任务后,对每结果调用 forget() 函数释放资源

result.get() 函数获取结果,result.forget() 在 backend 删掉该数据,也可设置:app.conf.update(result_expires=60),定时删除相应的结果。

报错:daemonic processes are not allowed to have children

原代码使用了多进程(multiprocessing),在与其他代码合并使用时产生了如上报错。

根据新逻辑可以不使用multiprocessing处理,删除后报错解决。worker是celery提供的任务执行单元,并发的运行在分布式系统节点中。

如果不方便修改,可参考如下方法

方法一:通过-P设置其他celery运行方式,但其他方式不是多进程模式,可能会影响项目性能

-P POOL, --pool POOL  Pool implementation: prefork (default), eventlet, gevent or solo.

方法二:设置环境变量,允许守护进程创建子进程

先执行  export PYTHONOPTIMIZE=1,再运行celery;如果在supervisord中托管celery,可在supervisord的配置文件中添加environment=PYTHONOPTIMIZE=1。

注意:PYTHONOPTIMIZE=1允许守护进程创建子进程,当主进程结束,会带走守护子进程,但守护子进程还来不及带走守护子进程的子进程(即使设置了daemon = True),守护子进程的子进程将会成为孤儿进程,需要特殊处理。

在celery中运行的代码需要添加装饰器 

启动celery前需要对计划运行在celery队列中的函数添加装饰器,如@app_celery.task,这样celery才能找到该函数,否则在运行该函数时报错:Received unregistered task of type 'tasks.add'.,KeyError: 'tasks.add'。

task_time_limit及soft_task_time_limit的使用

时间限制的配置方法:

在装饰器中添加time_limit的时间显示,其优先级高于配置文件,如:@app.task(time_limit=10)

在配置文件中设置超时的字段如下:

CELERYD_TASK_SOFT_TIME_LIMIT 
CELERYD_TASK_TIME_LIMIT

soft_time_limit 会在内部抛一个 Exception, hard time limit 没法被 catch.

celery pool 用的是 gevent, 现在的实现里 gevent 做 worker pool 的时候会忽略 soft_time_limit, 只有 hard_time_limit 会被触发(通过 gevent.Timeout 实现).

@app.task
def add(x, y):
    try:
        time.sleep(10)
        return x + y
    except SoftTimeLimitExceeded as stle:
        print(f"error: {stle}")
    except Exception as e:
        print(f"error: {e}")

如果出现soft超时异常,其报错信息显示在启动celery的界面,如果设置了保存结果的地址,如在redis中,因其异常已经捕获,其状态为success,但结果为空,如下:

如果出现hard超时异常,执行结果的状态为failure,异常被抛出,无法做后续处理:

查看celery中任务的数量及状态

1.  使用celery命令查看启动的应用(tasks)下正在执行的任务:celery -A tasks inspect active

2. 加入celery的任务,在执行delay()方法后会返回一个AsyncResult的类,包含任务的celery_task_id,可通过遍历的方法获取现有任务的状态。

3. 使用Flower监控工具

pip install flower,启动:celery -A tasks flower --port=5555

可在http://localhost:5555中查看到celery的状态和信息。

from celery.result import AsyncResult

from tasks import app

async_result = AsyncResult(id="", app=app)
print(f"Task status: {async_result.status}")
if async_result.successful():
    result = async_result.get()
    print(result)
elif async_result.failed():
    print("failed.")
elif async_result.status == "PENDING":
    print("PENDING")
elif async_result.status == "RETRY":
    print("Retrying")
elif async_result.status == "STARTED":
    print("Task Start.")

失败任务重试

from celery import shared_task
from celery.exceptions import Retry

@shared_task(bind=True, max_retries=3, default_retry_delay=5)
def fetch_data(self, url):
    try:
        response = requests.get(url)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as exc:
        # 如果失败则重试
        raise self.retry(exc=exc)

装饰器中rate_limit的含义

限制任务被每个worker在某个时间间隔内处理的最大次数,如rate limit = 10/m,即每分钟10次,如果worker数量为n,则每分钟内该任务可以被处理的最大次数为10 * n。

限制tasks模块下的add函数,每秒钟只能执行10次:
CELERY_ANNOTATIONS = {'tasks.add':{'rate_limit':'10/s'}}


限制所有的任务的刷新频率:
CELERY_ANNOTATIONS = {'*':{'rate_limit':'10/s'}}


设置任务执行失败后调用的函数

def my_on_failure(self,exc,task_id,args,kwargs,einfo):
    print('task failed')
 
CELERY_ANNOTATIONS = {'*':{'on_failure':my_on_failure}}

参考:

Celery中文文档

Celery - Distributed Task Queue — Celery 5.4.0 documentation (celeryq.dev)

Windows安装部署启动Celery,python_celery windows部署-CSDN博客

Configuration and defaults — Celery 4.4.7 documentation

解决celery报错daemonic processes are not allowed to have children-CSDN博客

查询celery正在执行的任务_celery查看任务状态-CSDN博客

python celery 失败任务重试 python的celery_mob6454cc6ff2b9的技术博客_51CTO博客

Celery Time Limit 的坑 · Shining Moon (monsterxx03.com)

python 异步任务 开始 停止 python异步任务框架_mob64ca140d61c6的技术博客_51CTO博客

Celery 分布式任务队列_celery查看任务队列-CSDN博客

celery笔记一之celery介绍、启动和运行结果跟踪_启动celery-CSDN博客

python celery 失败任务重试 python的celery_mob6454cc6ff2b9的技术博客_51CTO博客

【python】Celery 异步任务队列的高级用法_python怎么实现celery异步消息-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值