celery worker占用大内存记录
定位
top
命令下,M
键开启按MEM
列排序的进程列表,按H
则查看线程列表,由于worker
没有子线程,所以只显示一条记录;- 按
c
命令,开始COMMAND
列详细情况,可以找到对应的进程启动者; - 按
o
键,开启筛选功能,输入COMMAND=celery
只显示celery
名的进程列表;
top
的内存大小列为:VIRT
,RES
,分别为虚拟内存与实际内存占用;
到这里,就已经定位到了消耗内存的进程是哪些,启动者是哪个,如果还想看下进程间的关系,则使用命令:
ps -auxf
来查看进程之间的关系,通过上面的top
命令可以知道进程PID
,此时就可以通过PID
来过滤:
ps -auxf | grep PID -A 20 -B 20
在celery中,可以看到celery进程是以父子进程的形式管理的,父woker会创建多个子worker来执行任务,在显示的数据中能看到父worker与子worker的内存占用情况,如下图所示:
原因
- celery的worker进程是有工作时间与次数限制的,也就是说,如果调用次数到达一次数次,就会被回收,并重新创建(这里有错,见下面说明)。显然这里的情况就很有可能是进程没有被正常回收,那就可能是因为进程一直有个任务在运行,所以首先我去查询了对应的执行日志,结果发现日志一切正常,且worker的CPU也没显示在执行任务,所以这个排除;
错误点说明
默认情况下,celery的worker是没有对进程执行任务数作限制的,见官网说明:
Maximum number of tasks a pool worker process can execute before it’s replaced with a new one. Default is no limit.
所以需要自己配置worker_max_tasks_per_child
值,django
下配置:CELERYD_MAX_TASKS_PER_CHILD
- 其次我想到的是一些非正常网络连接或文件打开,确没有正常释放文件句柄,导致资源一直在消耗,所以我去检查下进程文件句柄的使用情况,采用如下命令:
lsof -n | grep 12987
过虑出该进程的句柄使用列表,结果发现其句柄数量正常:ps -aux | grep celery | wc -c
;
3. 没有办法了,只能祭出神器google了,查询到一篇不错的文章:不错的celery问题博客,该文章中提到了celery内存泄露的问题,但是写于2010年,版本已经非常落后,且他只说明了一个问题,supervisor执行restart
操作,不会使得子进程重启,所以建议大家使用stop
+start
操作,或是配置supervisor stopasgroup=true
,相关参考见:官方配置,issues,issues
经过上面这些无用的过程,最终结论还是,supervisorctl
下执行stop
+ start
…
还有官网下stop worker的命令,也就是建议使用kill -TERM PID
;
还有这篇文章:最大执行次数可以配置小点最大执行任务数CELERYD_MAX_TASKS_PER_CHILD
。