介绍
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 - 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博客

350

被折叠的 条评论
为什么被折叠?



