Django框架学习-Celery的使用

celery用户文档:https://docs.celeryq.dev/en/v5.3.1/userguide/index.html

1、Celery的提出

用户需要在网站填写注册信息,发给用户一封注册激活邮件到邮箱,如果由于各种原因,这封邮件发送所需时间较长,那么客户端将会等待很久,造成不好的用户体验。——> 将耗时任务放到后台异步执行,从而不影响用户其他操作。

功能:

  • 异步执行任务(发送邮件、调用第三方接口、批量处理文件)
  • 定时处理任务(清除缓存、备份数据库)

工作原理:

基于分布式消息传递的作业队列,

通常使用 broker(中间人)来协调 client(任务的发出者)和 worker(任务的处理者),clients发出消息到队列中,broker将队列中的信息派发给worker来处理。celery本身不提供消息服务,它支持的消息服务broker有RocketMQ和Redis。

2、Celery的执行流程

celery的运行由三部分组成,消息队列(message broker),任务执行单元(worker)和任务执行结果存储组成。

3、文件配置

目录结构

1 myproject/              # 服务端项目根目录
2 └── myproject/          # 主应用目录
3     ├── apps/           # 子应用存储目录  
4     ├   └── users/            # django的子应用
5     ├       └── tasks.py      # [新增]分散在各个子应用下的异步任务模块
6     ├── settings/     # [修改]django的配置文件存储目录[celery的配置信息填写在django配置中即可]
7     ├── __init__.py   # [修改]设置当前包目录下允许外界调用celery应用实例对象
8     └── celery.py     # [新增]celery入口程序,相当于上一种用法的main.py

celery.py文件,主目录下创建celery入口程序

 1 import os
 2 from celery import Celery
 3 
 4 # 为 celery 程序设置默认的 Django 配置
 5 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
 6 
 7 app = Celery('myproject')
 8 
 9 # 表示从 Django 的配置中加载 celery 的配置,namespace='CELERY' 表示 celery 的配置必须是以 'CELERY' 为前缀
10 app.config_from_object('django.conf:settings', namespace='CELERY')
11 
12 app.conf.update(
13     task_ignore_result=True
14 )
15 
16 # 自动根据配置查找django的所有子应用下的tasks任务文件
17 app.autodiscover_tasks()

setting.py,新增celery相关配置信息。

 1 # Celery异步任务队列框架的配置项[注意:django的配置项必须大写,所以这里的所有配置项必须全部大写]
 2 # 任务队列
 3 CELERY_BROKER_URL = 'redis://:123456@127.0.0.1:6379/14'
 4 # 结果队列
 5 CELERY_RESULT_BACKEND = 'redis://:123456@127.0.0.1:6379/15'
 6 # 时区,与django的时区同步
 7 CELERY_TIMEZONE = TIME_ZONE
 8 # 防止死锁
 9 CELERY_FORCE_EXECV = True
10 # 设置并发的worker数量
11 CELERYD_CONCURRENCY = 200
12 # 设置失败允许重试[这个慎用,如果失败任务无法再次执行成功,会产生指数级别的失败记录]
13 CELERY_ACKS_LATE = True
14 # 每个worker工作进程最多执行500个任务被销毁,可以防止内存泄漏,500是举例,根据自己的服务器的性能可以调整数值
15 CELERYD_MAX_TASKS_PER_CHILD = 500
16 # 单个任务的最大运行时间,超时会被杀死[慎用,有大文件操作、长时间上传、下载任务时,需要关闭这个选项,或者设置更长时间]
17 CELERYD_TIME_LIMIT = 10 * 60
18 # 任务发出后,经过一段时间还未收到acknowledge, 就将任务重新交给其他worker执行
19 CELERY_DISABLE_RATE_LIMITS = True
20 # celery的任务结果内容格式
21 CELERY_ACCEPT_CONTENT = ['json', 'pickle']
22  
23 # 之前定时任务(定时一次调用),使用了apply_async({}, countdown=30);
24 # 设置定时任务(定时多次调用)的调用列表,需要单独运行SCHEDULE命令才能让celery执行定时任务:celery -A mycelery.main beat,当然worker还是要启动的
25 # https://docs.celeryproject.org/en/stable/userguide/periodic-tasks.html
26 from celery.schedules import crontab
27 CELERY_BEAT_SCHEDULE = {
28     "user-add": {  # 定时任务的注册标记符[必须唯一的]
29         "task": "add",   # 定时任务的任务名称
30         "schedule": 10,  # 定时任务的调用时间,10表示每隔10秒调用一次add任务
31         # "schedule": crontab(hour=7, minute=30, day_of_week=1),,  # 定时任务的调用时间,每周一早上7点30分调用一次add任务
32     }
33 }

为了确保 celery 的 app 在 Django 运行的时候被加载,需要在 myproject/__init__.py 中引入 celery_app。

1 from .celery import app as celery_app
2 
3 __all__ = ('celery_app',)

redis和celery配置完成后,就可以编写任务了。

3、task定义

task.py文件可以位于myproject目录下,也可位于各个app的目录下。专属于某个Celery实例化项目的task可以使用@app.task装饰器定义,各个app目录下可以复用的task建议使用@shared_task定义。

 users/tasks.py

 1 from celery import shared_task
 2 from ronglianyunapi import send_sms as sms
 3 import logging
 4 logger = logging.getLogger("django")
 5 
 6 @shared_task(name="send_sms")
 7 def send_sms(tid, mobile, datas):
 8     """异步发送短信"""
 9     try:
10         return sms(tid, mobile, datas)
11     except Exception as e:
12         logger.error(f"手机号:{mobile},发送短信失败错误: {e}")
13 
14 @shared_task(name="send_sms1")
15 def send_sms1():
16     print("send_sms1执行了!!!")

users/views.py

 1 import random
 2 from django_redis import get_redis_connection
 3 from django.conf import settings
 4 # from ronglianyunapi import send_sms
 5 # from mycelery.sms.tasks import send_sms
 6 from .tasks import send_sms
 7  
 8 """
 9 /users/sms/(?P<mobile>1[3-9]\d{9})
10 """
11 class SMSAPIView(APIView):
12     """
13     SMS短信接口视图
14     """
15     def get(self, request, mobile):
16         """发送短信验证码"""
17         redis = get_redis_connection("sms_code")
18         # 判断手机短信是否处于发送冷却中[60秒只能发送一条]
19         interval = redis.ttl(f"interval_{mobile}")  # 通过ttl方法可以获取保存在redis中的变量的剩余有效期
20         if interval != -2:
21             return Response({"errmsg": f"短信发送过于频繁,请{interval}秒后再次点击获取!", "interval": interval},status=status.HTTP_400_BAD_REQUEST)
22  
23         # 基于随机数生成短信验证码
24         # code = "%06d" % random.randint(0, 999999)
25         code = f"{random.randint(0, 999999):06d}"
26         # 获取短信有效期的时间
27         time = settings.RONGLIANYUN.get("sms_expire")
28         # 短信发送间隔时间
29         sms_interval = settings.RONGLIANYUN["sms_interval"]
30         # 调用第三方sdk发送短信
31         # send_sms(settings.RONGLIANYUN.get("reg_tid"), mobile, datas=(code, time // 60))
32         # 异步发送短信
33         send_sms.delay(settings.RONGLIANYUN.get("reg_tid"), mobile, datas=(code, time // 60))
34  
35         # 记录code到redis中,并以time作为有效期
36         # 使用redis提供的管道对象pipeline来优化redis的写入操作[添加/修改/删除]
37         pipe = redis.pipeline()
38         pipe.multi()  # 开启事务
39         pipe.setex(f"sms_{mobile}", time, code)
40         pipe.setex(f"interval_{mobile}", sms_interval, "_")
41         pipe.execute()  # 提交事务,同时把暂存在pipeline的数据一次性提交给redis
42  
43         return Response({"errmsg": "OK"}, status=status.HTTP_200_OK)

 celery中异步任务发布的2个方法的参数如下:

1 异步任务名.delay(*arg, **kwargs)
2 异步任务名.apply_async((arg,), {'kwarg': value}, countdown=60, expires=120)

 

posted @ 2023-07-12 17:41  最咸的鱼  阅读(147)  评论(0编辑  收藏  举报