Celery
Tutorials
- broker
celery通过消息进行通信,通常使用一个叫Broker(中间人)来协client(任务的发出者)和worker(任务的处理者). clients发出消息到队列中,broker将队列中的信息派发给worker来处理。一个celery系统可以包含很多的worker和broker,可增强横向扩展性和高可用性能。
installation
pip install celery
or pip install celery --upgrade
gevent mode : pip install gevent
注意:
celery==5.2.3
, python==3.8
;
python 版本为3.7时,多进程会出现re-initialized错误。
run
- 不使用多进程、多线程模式,主要用来测试单独运行模型是否正常。
celery -A celery_main:celery worker -l DEBUG --concurrency 1 -P solo
celery: celery -A tasks worker --loglevel=info --pidfile=celery.pid --logfile=celery.log
using purge:
$celery_bin -A celery_main:celery worker -l debug -c $worker_num --purge
purge: Purge messages from all configured task queues.
kill
kill -INT [WorkerMainProcessID] # worker主进程的进程ID需要通过ps -ef | grep celery获取;或者通过 celery -A app inspect stats的输出结果获取
kill -TERM [WorkerMainProcessID]
flower
installation: pip install flower
run: celery -A celery_main:celery flower --port=8080
Runing Mode
pool eventlet: eventlet==0.25.1
;
pool gevnet: gevent==1.4
;
celery: celery-5.1.2
(Read the official documents in case of revised api )
Model Initialize
在prefork模式下,单独使用一个加载的模型不能实现多进程; 想要实现是的是对于每一个并发量,加载一个模型,即每一个worker根据并发量来控制模型的数量,从而使处理速度增加,负载均衡;
- 在每个worker前进行模型的加载;
@worker_process_init.connect()
def init_worker_process(**kwargs):
"""
load model before running tasks
:param kwargs:
:return:
"""
global pytorch_model
pytorch_model = load_model()
load_model()
必须是定义在同一个文件下,如果是不同文件,那么在init_worker_process中import相应文件;
from model_threads import loadModel
model_dict = loadModel()
ref:
Unable to use Pytorch with CUDA in Celery task
- 重写base task类方法;
注意在重写之后,对任务进行装饰器修饰需要添加bind=True
;
bind意味着该任务第一个参数是task instance(self),这样就能获得task instance中的各种属性和方法。
@task(bind=True, name="my_add")
def add(self, x, y):
return x + y
ref:
Serving ML Models in Production with FastAPI and Celery
Task
- task callback
class CustomTask(celery.Task):
def after_return(self, status, retval, task_id, args, kwargs, einfo):
try:
th = threading.Thread(target=push_results, args=(retval,))
th.start()
except Exception as e:
raise ActiveException(code=ErrorCode.ABNORMAL, msg='the after return function run error
- task cost time
@task_prerun.connect
def task_prerun_handler(signal, sender, task_id, task, args, kwargs):
tasks[task_id] = time()
@task_postrun.connect
def task_postrun_handler(signal, sender, task_id, task, args, kwargs, retval, state):
try:
cost = time() - tasks.pop(task_id)
ref:
per-task-name
Concurrency
celery中-c参数和-P参数
celery里面的-c参数指定的是并发度,而-P参数指定并发的实现方式,有 prefork(default)、eventlet、gevent等,prefork就是多进程的方式去实现并发。
problems
-
supervisord 中使用celery
在使用supervisord过程中,应该先退出supervisord,然后再进行celery的kill;- kill celery process
ps auxww | grep celery|grep -v grep|awk '{print $2}'
同下: - kill gunicorn process
ps -ef|grep gunicorn|grep -v grep|awk '{print $2}'|xargs -i kill -9 {}
- kill celery process
-
关于tasks结束后,redis依然贮留数据的问题;
对于异步的任务,可直接在任务装饰器中使用ignore_results即可,如@celery.task(base=CustomTask, ignore_result=True)
但是,对于同步任务,即使用get()
获得结果的任务,此方法无效,解决办法在是celery配置文件中设置redis_results过期时间,即result_expires = 300
;Don’t store task state. Note that this means you can’t use AsyncResult to check if the task is ready, or get its return value.
-
Received unregistered task of type 'tasks.tasks.detect_img_asy'.
文件层级结构问题;在celery_main
文件中的task路径,需要与主程序导入异步任务函数的路径相同;如:
from tasks.tasks import detect_invasion
和include=['tasks.tasks']
;- 装饰器任务为:
@celery_app.task
; - 在任务中的
name
属性可以声明确定具体路径。
@celery.task(name="modelPipline", ignore_result=True)
ref: 1
- 装饰器任务为:
-
ModuleNotFoundError: No module named 'celery.app.task'
- celery安装后重启服务;
-
celery TypeError: 'type' object is not subscriptable
- 切换至不同模式;
-
celery cannot combined with flower to start;
- 需要另启cmd来运行flower;
-
celery worker cannot start in srcipts by threaing or process;
-
RuntimeError: Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing, you must use the 'spawn' start method
ref: 1
-
fork 与 spawn 模式的区别;
这里有问题,就是 forked 是啥,spawn 又是啥?这里就需要了解创建子进程的方式了。
通过torch.multiprocessing.Process(target=training, args=(train_queue))
创建一个子进程fork和spawn是构建子进程的不同方式,区别在于
\1. fork: 除了必要的启动资源,其余的变量,包,数据等都集成自父进程,也就是共享了父进程的一些内存页,因此启动较快,但是由于大部分都是用的自父进程数据,所有是不安全的子进程。
\2. spawn:从头构建一个子进程,父进程的数据拷贝到子进程的空间中,拥有自己的Python解释器,所有需要重新加载一遍父进程的包,因此启动叫慢,但是由于数据都是自己的,安全性比较高。回到刚刚那个报错上面去。为啥提示要不能重复加载。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eh9iQy1o-1683875960593)(Notes/fork.png)]
-
CUDA initialization error
错误原因:- 这是因为Python3中使用 spawn启动方法才支持在进程之间共享CUDA张量。而用的multiprocessing 是使用 fork 创建子进程,不被 CUDA 运行时所支持。
- 在使用PyTorch框架搭建完网络,训练时出现CUDAerror(3):initializationerror(multiprocessing)错误,此类错误(若确定网络搭建没问题的话)多半是因为有其它并行程序正在运行,占用,导致在运行PyTorch程序时无法启动多线程,导致报错。
P.S 在celery使用过程中,利用
init_worker_process
来根据并发量来创建模型个数时,大概率会出现此问题,主要原因是在model.cuda
或者model.to(device)
之前,有其他CUDA的操作。例如,在Fairmot进行worker重复加载时,出现此错误主要是因为
import DCN
时会进行CUDA上的initialization操作;暂且未找到解决DCNv2包的解决办法,折衷处理是将原来的DCN替换为pytorch中自带的DCN模块,即from .dcn import DeformableConv2d as DCN
, 具体代码参考附录。4/1添加:
当使用fork模式时,主进程的变量被子进程变量共享,而CUDA的上下文环境是不支持这种共享的,因为CUDA每个子进程的变量都是独立的;
因此才会报re-initialized错误;
解决办法是:在init_worker_process中导入模型,包括:@worker_process_init.connect def init_worker_process(): model_id = "stabilityai/stable-diffusion-2" from diffusers import StableDiffusionPipeline, DDIMScheduler, EulerDiscreteScheduler # Use the Euler scheduler here instead scheduler = EulerDiscreteScheduler.from_pretrained(model_id, subfolder="scheduler") pipe = StableDiffusionPipeline.from_pretrained(model_id, scheduler=scheduler, torch_dtype=torch.float16)
是要统一写在init_worker下的,如果不是这样stablediffusionpipline是被其他子进程共享。
ref:
-
-
TypeError: __init__() got an unexpected keyword argument 'username'
-
celery中的redis使用localhost有问题,需要指明ip地址; -
celery中的redis安装一定需要按照官方文档安装!
$ pip install -U "celery[redis]"
-
-
设置多进程启动方法。
import torch.multiprocessing as mp mp.set_start_method('spawn')
此方法是使用multiprocessing时需要使用pytorch模型时,需要设置的方法;但是,如果使用fork似乎也运行正常。
-
CUDA kernel errors might be asynchronously reported at some other API call,so the stacktrace below might be incorrect.
直接运行时
export CUDA_LAUNCH_BLOCKING=1
; -
运行过程中提升没有torch
- 主要是因为安装celery的时候,是以
-U
的形式安装在.local
, 而在此环境中没有安装pytorch;因此,需要使用在特定的anaconda环境下安装celery,然后使用celery命令时指明位置,如(zzzj_test) zzzj@alpha-AI:~/Projects/task_intrusion_celery/server$ /home/zzzj/.conda/envs/zzzj_nni/bin/celery -A celery_main:celery worker -l INFO -c 1 -P solo
- 主要是因为安装celery的时候,是以
-
celery使用redis作为broker,报错ConnectionError: Too many connections
主要是在进行高并发测试时,解决方法为将celery.conf.broker_pool_limit = 20
数目调大,默认为10. -
TypeError: Object of type ndarray is not JSON serializable
主要原因是redis中储存的数据不能是自定义的数据类型或者numpy.array类型,对于图像数据转换为base64,对于自定义数据类型,使用object.__dict__
以字符串形式传递。 -
ImportError:cannot import name ‘Celery‘ from ‘celery
python 版本和celery版本问题。 -
RuntimeError: Never call result.get() within a task!
使用celery canvas中的group任务类型实现分组任务 -
Celery批量异步调用任务一直等待结果
使用group异步调用时,如果一直等待结果,部分原因是任务没有保存结果,即ignore_result=True; 应该设置为默认值False; -
使用两个celery时,会出现
Received unregistered task of type 'celery.atomTask'.
-
Celery: stuck in infinitly repeating timeouts (Timed out waiting for UP message)
主要是worker在init时耗时过长,提升PROC_ALIVE_TIMEOUT
时长; -
RuntimeWarning:You're running the worker with superuser privileges:this is absolutely not recommended
from celery import platforms platforms.C_FORCE_ROOT=True
或者使用uid,ugroup
exec celery --app=app worker \ --loglevel=INFO --logfile=/var/log/celery/worker-example.log \ --statedb=/var/run/celery/worker-example@%h.state \ --hostname=worker-example@%h \ --queues=celery.example -O fair \ --uid=nobody --gid=nogroup
当两者无效时,使用multi运行时,就可以了。
-
celery在docker中使用时,会出现stuck现象,
主要原因是docker需要使用本机的redis;(保留)
ref: