前几天在工作中遇到djcelery定时任务失效的问题,查了好几天,最终定位解决,整理分享下
首先简单介绍下djcelery定时任务的框架,估计了解它的人都很熟悉,如下图
其实简单的说就是celery的beat定时将任务发给消息中间件(这里用的是rabbitmq队列),rabbitmq将定时任务发送给celery worker去执行(即django中的task),最终将检测结果存储的过程。
关于celery的详细介绍可见http://python.jobbole.com/87086/
问题现象如下:
发现一直正常运行的定时任务没有正常执行,而celery的worker和beat进程都在后台运行中,即如下的进程是存在的
故曾经的运维经验告诉我要重启,所以,第一招,重启celery的worker和beat进程,但发现问题依旧,也就是说这个问题没法通过守护进程的方式避免。
那第二招就是看日志了,查看worker的日志,发现其实worker已经准备好,但一直没有真正去执行任务
下一步就是查看发生任务的消息中间件了,就是神奇的rabbitmq,rabbitmq的情况可在网页查看,如下图
可以明显的看到queue message是没有数据的,但message rates却是有数据的(这个点其实还是没弄清到底是为什么,欢迎比较懂的同学指点)
故尝试手动执行celery beat和worker,rabbitmq是否能收到任务,发现在djcelery的任务没有发送到rabbitmq中,手动编写测试task程序
celeryconfig.py
import sys import os sys.path.insert(0, os.getcwd()) CELERY_IMPORTS = ("tasks", ) CELERY_RESULT_BACKEND = "amqp" BROKER_HOST = "localhost" BROKER_PORT = 5672 BROKER_USER = "guest" BROKER_PASSWORD = "guest" BROKER_VHOST = "/"
tasks.py
from celery.task import task from celery import Celery, platforms platforms.C_FORCE_ROOT = True @task def add(x, y): return x + y
执行任务,发现rabbitmq可以接受到消息,由于之前对队列的了解相当少,这里也请教了些对队列比较了解的同学,都觉得应该是消费者配置的问题,但查了django的setting没看到异常。
查了下网上关于rabbitmq的资料,大概是生产者通过exchange将消息按照规则发送给对应的队列,而我们的队列是没有收到消息的,修改了exchange规则调试,确实队列中会收到消息,但这个队列不是我们系统制定的队列。
在和rabbitmq纠缠了好久后,各种执行celery的命令,发现手动单独执行celery beat时会出现如下报错
怀疑与定时任务有关,查看了报错的celery的schedules.py文件中报错的位置,初步判断是定时任务设定的问题,通过django的admin查看Periodic task的任务设置情况,发现有一个测试任务没有设置crontab和Interval,之前就遇到过当进入到这个页面,如果两个都不设置报错就会报错的情况,故将interval改为定时监测时间,crontab不设置后,celery worker日志中有数据了,rabbitmq的队列也恢复正常了。
其实这个问题在这个文档中有一些说明
https://my.oschina.net/kinegratii/blog/292395?fromerr=2lvw3H0L
定时任务的重点在于必须有且仅有interval和crontab中的其一设置,即不能同时设置也不能同时不设置
celery很强大,更多的情况可直接参考上述的两个文档,基本够用了
而为什么会出现测试任务的定时设置的crontab和interval都为空的情况呢?由于我们的系统使用的开源项目为页面式的,在调试新功能时增加的测试任务,而修改没有涉及到后续任务执行情况的改动,故没有在admin页面设定interval和crontab,导致celery beat启动报错。
为了避免这类的问题再次出现,进行如下的一些改进
1、在系统页面中增加添加任务后自动设置默认的crontab和interval(省得人工去django的admin改,当然,django的admin很强大)
2、增加celery beat单独的日志
最后,无变更不故障,这句话我是真的体会到了