vue-django实现云通讯短信验证码四Celery 异步解决发送短信验证码耗时操作

一、问题引入

  • 我们的代码是自上而下同步执行的。
  • 发送短信是耗时的操作。如果短信被阻塞住,用户响应将会延迟。
  • 响应延迟会造成用户界面的倒计时延迟。

二、解决方案

  • 异步发送短信
  • 发送短信和响应分开执行,将发送短信从主业务中解耦出来

三、生产者消费者设计模式介绍

  • 为了将发送短信从主业务中解耦出来,可以引入生产者消费者设计模式
  • 它是最常用的解耦方式之一,寻找中间人(broker)搭桥,保证两个业务没有直接关联
  • 生产者生成消息,缓存到消息队列中,消费者读取消息队列中的消息并执行。
  • 由用户在前端点击发送验证码后端生成发送短信消息,缓存到消息队列中,消费者读取消息队列中的发送短信消息并执行。

四、Celery介绍 

1、介绍:

  • 一个简单、灵活且可靠、处理大量消息的分布式系统,可以在一台或者多台机器上运行。
  • 单个 Celery 进程每分钟可处理数以百万计的任务。
  • 通过消息进行通信,使用消息队列(broker)客户端消费者之间进行协调。

2、celery的5个角色:

Task:消息,任务,需要进行异步的函数或其他操作

Broker:中间人,接收生产者发来的任务即Task,将任务存入队列。任务的消费者Worker。Celery本身不提供队列服务,推荐用Redis或RabbitMQ实现队列服务

Worker:执行任务的单元,它实时监控消息队列,如果有任务就获取任务并执行它。

Beat:定时任务调度器,根据配置定时将任务发送给Broker。

Backend:用于存储任务的执行结果
 

五、定义发送短信任务

1、window下安装celery

pip install -U Celery

  • celery默认是进程池方式,进程数以当前机器的CPU核数为参考,每个CPU开四个进程。
  • 由于celery4.x后好像不支持win系统,所以这里我们使用线程的方式

2、window下安装eventlet

pip install eventlet

3、定义celery目录结构

4、定义配置文件config.py 

# 指定消息队列为redis(中间人,中间容器)
broker_url = 'redis://mast:{}@xx.xx.xx.xx:6379/10'.format(123456)
# 指定消费者结果存储redis
backend = 'redis://mast:{}@xx.xx.xx.xx:6379/11'.format(123456)

5、定义主文件main.py

# celery启动类模块
from celery import Celery

# 创建celery实例
celery_app = Celery('TestDjango')
# 加载celery配置
celery_app.config_from_object('celery_tasks.config')
# 注册任务到celery实例中
celery_app.autodiscover_tasks(['celery_tasks.sms'])
"""
也可以通过以下方式配置中间人
    celery_app = Celery(
        'TestDjango',  # 指定实例名,
        # 指定消息队列为redis(中间人,中间容器)
        broker_url='redis://mast:{}@xx.xx.xx.xx:6379/10'.format(123456),
        # 指定消费者结果存储redis
        backend='redis://mast:{}@xx.xx.xx.xx:6379/11'.format(123456),
    )
    celery_app.conf.update(
        # 指定消息队列为redis(中间人,中间容器)
        broker_url='redis://mast:{}@xx.xx.xx.xx:6379/10'.format(123456),
        # 指定消费者结果存储redis
        backend='redis://mast:{}@xx.xx.xx.xx:6379/11'.format(123456),
    )
"""

6、定义sms包下的tasks.py发送短信验证码任务(注意:定义的任务py文件名必须为tasks.py

不带参数配置

from celery_tasks.sms_code.ronglian_sms_sdk import SmsSDK
from celery_tasks import utils
import logging
from celery_tasks.main import celery_app

# 创建容通讯实例对象
sms_code = SmsSDK(accId=utils.ACCID, accToken=utils.ACCTOKEN, appId=utils.APPID)


@celery_app.task
def celery_send_sms_code(mobile, code):
    """
    :param mobile: 电话号码
    :param code: 短信验证码
    :return: 成功0 或-1
    """
    try:
        send_result = sms_code.sendMessage(tid='1', mobile=mobile, datas=(code, 5))
        print('函数正在测试')
    except Exception as e:
        logging.error(e)
    else:
        return send_result

带参数配置

from celery_tasks.sms_code.ronglian_sms_sdk import SmsSDK
from celery_tasks import utils
import logging
from celery_tasks.main import celery_app

# 创建容通讯实例对象
sms_code = SmsSDK(accId=utils.ACCID, accToken=utils.ACCTOKEN, appId=utils.APPID)


# bind:保证task对象会作为第一个参数自动传入
# name:异步任务别名
# retry_backoff:异常自动重试的时间间隔 第n次(retry_backoff×2^(n-1))s
# max_retries:异常自动重试次数的上限
@celery_app.task(bind=True, name='send_sms_code', retry_backoff=3)
def celery_send_sms_code(self,mobile, code):
    """
    :param mobile: 电话号码
    :param code: 短信验证码
    """
    try:
        send_result = sms_code.sendMessage(tid='1', mobile=mobile, datas=(code, 5))
        print('函数正在测试')
    except Exception as e:
        logging.error(e)
        # 有异常自动重试三次
        raise self.retry(exc=e, max_retries=3)
    if not send_result:
        raise self.retry(exc=Exception('发送短信失败'), max_retries=3)
    else:
        return send_result

 6、在终端下启动celery服务

# 进程方式启动
celery -A celery_tasks.main worker -l info
# 线程方式启动
celery -A celery_tasks.main worker -l info --concurrency=1000 -P eventlet -c 1000
  • -A:指对应的应用程序, 其参数是项目中 Celery实例的位置。
  • worker:指这里要启动的worker。
  • -l指日志等级,比如info等级
  • -P:运行的程序,可以是threads、gevent、eventlet等
  • -c:任务的并行数
  • --concurrency:指定每个CPU开的进程数

7、视图中调用发送短信任务 

celery_send_sms_code.delay(mobile=mobile, code=sms_code)
class SmsCodeView(View):
    """
    获取短信验证码
    """
    def get(self, request, mobile):
        # 获取redis连接对象,存储短信验证码
        try:
            redis_conn = get_redis_connection('verify_code')
        except DatabaseError as e:
            return http.JsonResponse({
                'errmsg': '获取验证码失败'
            })
        # 获取发送标志
        send_flag = redis_conn.get('send_flag_%s' % mobile)
        # 如果发送标志存在,说明用户在60秒内多次获取验证码
        if send_flag:
            return http.JsonResponse({
                'errmsg': '发送短信验证码过于频繁'
            })
        # 生成随机6为验证码
        sms_code = '%06d' % random.randint(0, 999999)
        # 存储随机验证码(五分钟的有效期)
        redis_conn.setex('sms_code_%s' % mobile, 300, sms_code)
        # 设置发送标志
        redis_conn.setex('send_flag_%s' % mobile, 60, 1)

        # 调用发送短信验证码,通过delay()方法
        celery_send_sms_code.delay(mobile=mobile, code=sms_code)
        # 返回响应
        response = http.JsonResponse({
            'errmsg': 'ok'
        })
        response.headers['Access-Contro1-Allow-origin'] = '*'
        return response

    def post(self, request, mobile):
        """
        验证短信验证码
        """
        # 接受参数
        json_dict = json.loads(request.body.decode())
        sms_code_client = json_dict.get('sms_code')
        if not sms_code_client:
            return http.JsonResponse({
                'errmsg': '验证错误'
            })
        try:
            redis_conn = get_redis_connection('verify_code')
            sms_code_server = redis_conn.get('sms_code_%s' % mobile)
        except DatabaseError as e:
            print(e)
            return http.JsonResponse({
                'errmsg': '验证错误'
            })
        if sms_code_server is None or sms_code_server != sms_code_client:
            return http.JsonResponse({
                'errmsg': '验证错误或图形验证码失效'
            })
        # 验证成功后删除验证码
        redis_conn.delete('sms_code_%s' % mobile)
        return http.JsonResponse({
            'errmsg': 'ok'
        })

六、测试 

 

 

上一篇: vue-django实现(云通讯短信验证码)(三)前后端设置60秒内发送短信验证码限制

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340