原文:【精选】Django+Celery+Flower实现异步和定时任务及其监控告警_django flower-CSDN博客
这篇文章,原作者图文并茂的方式讲解了在Django中如何使用Celery如何执行异步任务和定时任务,我按照这篇文章的操作顺序很好的实现了异步任务。
希望大家多多支持原作者,我也是在很多文章中才发现了这一篇如此优质的文章。
原文地址:Django+Celery+Flower实现异步和定时任务及其监控告警 | XieJava's blog
文章的第7节是我这个入门萌新的体会,欢迎指正和讨论。
原作者微信公众号:
用Django框架进行web开发非常的快捷方便,但Django框架请求/响应是同步的。但我们在实际项目中经常会碰到一些耗时的不能立即返回请求结果任务如:数据爬取、发邮件等,如果常时间等待对用户体验不是很好,在这种情况下就需要实现异步实现,马上返回响应请求,但真正的耗时任务在后台异步执行。Django框架本身无法实现异步响应但可以通过Celery很快的实现异步和定时任务。本文将介绍如何通过Django+Celery+Flower实现异步和定时任务及其任务的监控告警。
常见的任务有两类,一类是异步任务,一类是定时任务(定时执行或按一定周期执行)。Celery都能很好的支持。
Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celery, 举几个实例场景中可用的例子:
Celery 在执行任务时需要通过一个消息中间件(Broker)来接收和发送任务消息,以及存储任务结果, 一般使用rabbitMQ、Redis或其他DB。
本文使用redis作为消息中间件和结果存储,在后面的通过数据库监控任务执行案例将介绍用到数据库作为结果存储。
1 2 3 |
pip install celery pip install redis pip install eventlet #在windows环境下需要安装eventlet包 |
在主项目目录下,新建celary.py文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import os import django from celery import Celery from django.conf import settings # 设置系统环境变量,否则在启动celery时会报错 # taskproject 是当前项目名 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'taskproject.settings') django.setup() celery_app = Celery('taskproject') celery_app.config_from_object('django.conf:settings') celery_app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) |
在主目录的init.py中添加如下代码:
1 2 3 |
from .celery import celery_app __all__ = ['celery_app'] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
###----Celery redis 配置-----### # Broker配置,使用Redis作为消息中间件 BROKER_URL = 'redis://:[email protected]:6379/0' # BACKEND配置,使用redis CELERY_RESULT_BACKEND = 'redis://:[email protected]:6379/1' CELERY_ACCEPT_CONTENT=['json'] CELERY_TASK_SERIALIZER='json' # 结果序列化方案 CELERY_RESULT_SERIALIZER = 'json' # 任务结果过期时间,秒 CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24 # 时区配置 CELERY_TIMEZONE = 'Asia/Shanghai' |
这时候Celery的基本配置完成了,可以实现并添加任务了。
在子应用下建立各自对应的任务文件tasks.py(必须是tasks.py这个名字,不允许修改
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import datetime from time import sleep from celery import shared_task import logging logger = logging.getLogger(__name__) @shared_task() def task1(x): for i in range(int(x)): sleep(1) logger.info('this is task1 '+str(i)) return x @shared_task def scheduletask1(): now = datetime.datetime.now() logger.info('this is scheduletask '+now.strftime("%Y-%m-%d %H:%M:%S")) return None |
在tasks.py中我们定义了两个任务,这两个任务要用@shared_task装饰起来,否则celery无法管理。
为了放便执行我们通过views把这两个任务通过函数方法调用起来,用URL进行发布。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
views.py from django.http import JsonResponse from . import tasks # Create your views here. def runtask(request): x=request.GET.get('x') tasks.task1.delay(x) content= {'200': 'run task1 success!---'+str(x)} return JsonResponse(content) def runscheduletask(request): tasks.scheduletask1.delay() content= {'200': 'success!'} return JsonResponse(content) |
在urls中加入路由进行发布
1 2 3 4 5 6 7 8 |
from django.urls import path from taskapp import views urlpatterns = [ path('task', views.runtask), path('runscheduletask', views.runscheduletask), ] |
在项目的主urls中加入子项目的urls
在启动celery之前,先要启动redis服务,因为celery在settings中配置要用到redis作为消息中间件和结果存储。
windows环境下启动redis的命令为redis-server.exe redis.windows.conf
在控制台启动celery的worker
1 |
celery -A taskproject worker -l debug -P eventlet |
启动django访问url调用任务,看异步效果
控制台查看异步任务执行的情况,可以看web的url很快返回响应结果,后台控制台一直在执行异步任务。
Celery实现定时任务也很方便
在settings.py中加入定时任务的定义就可以实现定时任务
1 2 3 4 5 6 7 8 9 10 11 |
from celery.schedules import crontab CELERYBEAT_SCHEDULE = { 'every_5_seconds': { # 任务路径 'task': 'taskapp.tasks.scheduletask1', # 每5秒执行一次 'schedule': 5, 'args': () } } |
这里这个scheduletask1是前面tasks.py中定义的任务,当然也可以定义多个定时任务,如加一个task1,task1是有参数的,可以在’args’: ()中传入参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
CELERYBEAT_SCHEDULE = { 'every_5_seconds': { # 任务路径 'task': 'taskapp.tasks.scheduletask1', # 每5秒执行一次 'schedule': 5, 'args': () }, 'every_10_seconds': { # 任务路径 'task': 'taskapp.tasks.task1', # 每10秒执行一次,task1的参数是5 'schedule': 10, 'args': ([5]) } } |
这里定义了task1是10秒执行一次,传入的参数是5。
需要保持worker进程,另外开一个控制台启动beat
1 |
celery -A taskproject beat -l debug |
启动任务后看控制台打印的日志task1和scheduletask1都按计划定时执行了。
虽然通过settings.py的配置可以实现定时任务的配置,做为实际项目中可能还是不够实用,更加工程化的做法是将定时任务的配置放到数据库里通过界面来配置。同样Celery对此也提供了很好的支持,这需要安装django-celery-beat插件。以下将介绍使用过程。
1 |
pip install django-celery-beat |
1 2 3 4 |
INSTALLED_APPS = [ .... 'django_celery_beat', ] |
在settings.py中屏蔽到原来的调度器,加入
1 |
CELERYBEAT_SCHEDULER = 'django_celery_beat.schedulers.DatabaseScheduler' |
在setings.py中设置好语言、时区等
1 2 3 4 5 6 7 |
LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_TZ = False |
1 |
python manage.py migrate django_celery_beat |
在两个控制台分别启动woker和beta
1 |
celery -A taskproject worker -l debug -P eventlet |
1 |
celery -A taskproject beat -l debug |
访问 http://localhost:8000/admin/ 可以看到周期任务的管理菜单,管理定时任务非常方便。
点击“间隔”
点击“增加间隔”来增加定时任务的配置,增加一个5秒执行一次的定时器。
看到有个每5秒的定时器
这时可以用这个定时器去新建调度任务了。选择周期性任务,点击“增加周期性任务”
填入任务名,选择需要定时执行的任务
因为task1需要参数,在后面参数设置中进行参数的设置。
保存后可以看到新加了一条“每5秒执行一次task1”的调度任务。
在woker和beta的控制台都可以看到有定时任务执行的信息,说明任务被成功调度执行了。
在控制台监控任务执行情况,还不是很方便,最好是能够通过web界面看到任务的执行情况,如有多少任务在执行,有多少任务执行失败了等。这个Celery也是可以做到了,就是将任务执行结果写到数据库中,通过web界面显示出来。这里要用到django-celery-results插件。通过插件可以使用Django的orm作为结果存储,这样的好处在于我们可以直接通过django的数据查看到任务状态,同时为可以制定更多的操作,下面介绍如何使用orm作为结果存储。
1 |
pip install django-celery-results |
1 2 3 4 |
INSTALLED_APPS = ( ..., 'django_celery_results', ) |
1 2 3 4 |
# BACKEND配置,使用redis #CELERY_RESULT_BACKEND = 'redis://:[email protected]:6379/1' # 使用使用django orm 作为结果存储 CELERY_RESULT_BACKEND = 'django-db' #使用django orm 作为结果存储 |
1 |
python manage.py migrate django_celery_results |
可以看到创建了django_celery_results相关的表
启动django服务后,执行异步和定时任务,就可以在管理界面看到任务的执行情况,执行了哪些任务,哪些任务执行失败了等。
如果不想通django的管理界面监控任务的执行,还可以通过Flower插件来进行任务的监控。FLower的界面更加丰富,可以监控的信息更全。以下介绍通过Flower来进行任务监控。
1 |
pip install flower |
1 |
celery -A taskproject flower --port-5566 |
点击失败的我们可以看到执行失败的详情,这里是故意给task1的参数传了个‘a’字符,导致它执行报错了。可以看到任务执行的报错信息也展示出来了。
虽然可以通过界面来监控了,但是我们想要得更多,人不可能天天盯着界面看吧,如果能实现任务执行失败就自动发邮件告警就好了。这个Celery当然也是没有问题的。
通过钩子程序在异常的时候触发邮件通知。
对tasks.py的改造如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
import datetime from time import sleep from celery import shared_task from celery import Task from django.core.mail import send_mail import logging logger = logging.getLogger(__name__) class MyHookTask(Task): def on_success(self, retval, task_id, args, kwargs): info=f'任务成功-- 0task id:{task_id} , arg:{args} , successful !' logger.info(info) send_mail('celery任务监控', info, '[email protected]', ['[email protected]']) def on_failure(self, exc, task_id, args, kwargs, einfo): info=f'任务失败-- task id:{task_id} , arg:{args} , failed ! erros: {exc}' logger.info(info) send_mail('celery任务监控异常', info, '[email protected]', ['[email protected]']) def on_retry(self, exc, task_id, args, kwargs, einfo): logger.info(f'task id:{task_id} , arg:{args} , retry ! erros: {exc}') @shared_task(base=MyHookTask, bind=True) def task1(self,x): for i in range(int(x)): sleep(1) logger.info('this is task1 '+str(i)) return x @shared_task def scheduletask1(): now = datetime.datetime.now() logger.info('this is scheduletask '+now.strftime("%Y-%m-%d %H:%M:%S")) return None |
将work和beta服务关掉,在两个控制台分别重新启动woker和beta
1 |
celery -A taskproject worker -l debug -P eventlet |
1 |
celery -A taskproject beat -l debug |
在任务成功或失败的时候发邮件通知。
任务执行成功通知
任务执行异常告警通知
Django如何发送邮件见 通过Django发送邮件_django 发送邮件-CSDN博客
至此,本文通过几个简单的应用介绍了Django+Celery+Flower实现异步和定时任务及其监控告警。
任务执行成功通知
任务执行异常告警通知
Django如何发送邮件见 通过Django发送邮件_django 发送邮件-CSDN博客
至此,本文通过几个简单的应用介绍了Django+Celery+Flower实现异步和定时任务及其监控告警。
django应用在这个框架中,担任的是http应用角色,前端通过http请求,django的view对http请求处理,然后通过tasks.task1.delay(x)生产任务,发到redis数据库中。django只起到一个处理请求,连接celery应用的作用,生产任务(生产者),django进程不会有任何有关任务执行的打印,所以你想在django中看到任务的执行是不可能的,这也是调试的难点所在。
真正的消费者是通过命令行方式执行的celery服务,如下:
celery -A taskproject worker -l debug -P eventlet
如果你仅仅启动django应用,不启动celery服务,那么这个任务是不会被消费的。redis数据库中key为celery的队列里面一直会有多个任务(django view里发起的),如果不开celery服务,那么这些任务是不会消费的。如下图:
如果开启了celery服务,celery才会对队列里的任务进行消费,然后会生成key为celery-task-meta-taskid的
所以celery是一个独立的消费者进程。和django进程没有关系(任务的执行和django进程没有关系)。
实践体会难免片面,欢迎指正勘误。
原作者微信公众号