Python_美多商城(验证码)_3

图形验证码逻辑分析

Python_美多商城(验证码)_3_第1张图片

需要新建应用verifications

知识要点

  1. 将图形验证码的文字信息保存到Redis数据库,为短信验证码做准备。
  2. UUID 用于唯一区分该图形验证码属于哪个用户,也可使用其他唯一标识信息来实现。

========================

图形验证码接口设计和定义

1. 图形验证码接口设计

1.请求方式

选项 方案
请求方法 GET
请求地址 image_codes/(?P[\w-]+)/

2.请求参数:路径参数

参数名 类型 是否必传 说明
uuid string 唯一编号

3.响应结果:image/jpg

2. 图形验证码接口定义

1.图形验证码视图

class ImageCodeView(View):
    """图形验证码"""

    def get(self, request, uuid):
        """
        :param request: 请求对象
        :param uuid: 唯一标识图形验证码所属于的用户
        :return: image/jpg
        """
        pass

2.总路由

# verifications
url(r'^', include('verifications.urls')),

3.子路由

# 图形验证码
url(r'^image_codes/(?P[\w-]+)/$', views.ImageCodeView.as_view()),

====================

图形验证码后端逻辑

1. 准备captcha扩展包

提示:captcha扩展包用于后端生成图形验证码

Python_美多商城(验证码)_3_第2张图片

可能出现的错误

  • 报错原因:环境中没有Python处理图片的库:PIL

解决办法

  • 安装Python处理图片的库:pip install Pillow

2. 准备Redis数据库

准备Redis的2号库存储验证码数据

"verify_code": { # 验证码
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/2",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },

3. 图形验证码后端逻辑实现

class ImageCodeView(View):
    """图形验证码"""

    def get(self, request, uuid):
        """
        :param request: 请求对象
        :param uuid: 唯一标识图形验证码所属于的用户
        :return: image/jpg
        """
        # 生成图片验证码
        text, image = captcha.generate_captcha()

        # 保存图片验证码
        redis_conn = get_redis_connection('verify_code')
        redis_conn.setex('img_%s' % uuid, constants.IMAGE_CODE_REDIS_EXPIRES, text)

        # 响应图片验证码
        return http.HttpResponse(image, content_type='image/jpg')

=========================

图形验证码前端逻辑

1. Vue实现图形验证码展示

1.register.js

mounted(){
    // 生成图形验证码
    this.generate_image_code();
},
methods: {
    // 生成图形验证码
    generate_image_code(){
        // 生成UUID。generateUUID() : 封装在common.js文件中,需要提前引入
        this.uuid = generateUUID();
        // 拼接图形验证码请求地址
        this.image_code_url = "/image_codes/" + this.uuid + "/";
    },
    ......
}

2.register.html

  • 图形验证码 请填写图形验证码
  • 3.图形验证码展示和存储效果

    Python_美多商城(验证码)_3_第3张图片

    2. Vue实现图形验证码校验

    1.register.html

  • 图形验证码 [[ error_image_code_message ]]
  • 2.register.js

    check_image_code(){
        if(!this.image_code) {
            this.error_image_code_message = '请填写图片验证码';
            this.error_image_code = true;
        } else {
            this.error_image_code = false;
        }
    },
    

    3.图形验证码校验效果

    ====================

    短信验证码

    短信验证码逻辑分析

    Python_美多商城(验证码)_3_第4张图片

    知识要点

    1. 保存短信验证码是为注册做准备的。
    2. 为了避免用户使用图形验证码恶意测试,后端提取了图形验证码后,立即删除图形验证码。
    3. Django不具备发送短信的功能,所以我们借助第三方的容联云通讯短信平台来帮助我们发送短信验证码。

    ==========================

    容联云通讯短信平台

    1. 容联云通讯短信平台介绍

    1.容联云官网

    • 容联云通讯网址:https://www.yuntongxun.com/
    • 注册并登陆

    Python_美多商城(验证码)_3_第5张图片

    2.容联云管理控制台

    Python_美多商城(验证码)_3_第6张图片

    3.容联云创建应用

    Python_美多商城(验证码)_3_第7张图片

    Python_美多商城(验证码)_3_第8张图片

    4.应用申请上线,并进行资质认证

    Python_美多商城(验证码)_3_第9张图片

    Python_美多商城(验证码)_3_第10张图片

    5.完成资质认证,应用成功上线

    Python_美多商城(验证码)_3_第11张图片

    Python_美多商城(验证码)_3_第12张图片

    6.添加测试号码

    Python_美多商城(验证码)_3_第13张图片

    Python_美多商城(验证码)_3_第14张图片

    7.短信模板

    Python_美多商城(验证码)_3_第15张图片

    Python_美多商城(验证码)_3_第16张图片

    2. 容联云通讯短信SDK测试

    1.模板短信SDK下载

    • https://www.yuntongxun.com/doc/ready/demo/1_4_1_2.html

    2.模板短信SDK使用说明

    • http://doc.yuntongxun.com/p/5a533e0c3b8496dd00dce08c

    3.集成模板短信SDK

    • CCPRestSDK.py:由容联云通讯开发者编写的官方SDK文件,包括发送模板短信的方法
    • ccp_sms.py:调用发送模板短信的方法

    Python_美多商城(验证码)_3_第17张图片

    4.模板短信SDK测试

    • ccp_sms.py文件中
    # -*- coding:utf-8 -*-
    
    from verifications.libs.yuntongxun.CCPRestSDK import REST
    
    # 说明:主账号,登陆云通讯网站后,可在"控制台-应用"中看到开发者主账号ACCOUNT SID
    _accountSid = '8aaf070862181ad5016236f3bcc811d5'
    
    # 说明:主账号Token,登陆云通讯网站后,可在控制台-应用中看到开发者主账号AUTH TOKEN
    _accountToken = '4e831592bd464663b0de944df13f16ef'
    
    # 请使用管理控制台首页的APPID或自己创建应用的APPID
    _appId = '8aaf070868747811016883f12ef3062c'
    
    # 说明:请求地址,生产环境配置成app.cloopen.com
    _serverIP = 'sandboxapp.cloopen.com'
    
    # 说明:请求端口 ,生产环境为8883
    _serverPort = "8883"
    
    # 说明:REST API版本号保持不变
    _softVersion = '2013-12-26'
    
    # 云通讯官方提供的发送短信代码实例
    # 发送模板短信
    # @param to 手机号码
    # @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 ''
    # @param $tempId 模板Id
    def sendTemplateSMS(to, datas, tempId):
        # 初始化REST SDK
        rest = REST(_serverIP, _serverPort, _softVersion)
        rest.setAccount(_accountSid, _accountToken)
        rest.setAppId(_appId)
    
        result = rest.sendTemplateSMS(to, datas, tempId)
        print(result)
        for k, v in result.items():
    
            if k == 'templateSMS':
                for k, s in v.items():
                    print('%s:%s' % (k, s))
            else:
                print('%s:%s' % (k, v))
    
    if __name__ == '__main__':
        # 注意: 测试的短信模板编号为1
        sendTemplateSMS('17600992168', ['123456', 5], 1)
    

    5.模板短信SDK返回结果说明

    {
        'statusCode': '000000', // 状态码。'000000'表示成功,反之,失败
        'templateSMS': 
            {
                'smsMessageSid': 'b5768b09e5bc4a369ed35c444c13a1eb', // 短信唯一标识符
                'dateCreated': '20190125185207' // 短信发送时间
            }
    }
    

    3. 封装发送短信单例类

    1.封装发送短信单例类

    class CCP(object):
        """发送短信的单例类"""
    
        def __new__(cls, *args, **kwargs):
            # 判断是否存在类属性_instance,_instance是类CCP的唯一对象,即单例
            if not hasattr(CCP, "_instance"):
                cls._instance = super(CCP, cls).__new__(cls, *args, **kwargs)
                cls._instance.rest = REST(_serverIP, _serverPort, _softVersion)
                cls._instance.rest.setAccount(_accountSid, _accountToken)
                cls._instance.rest.setAppId(_appId)
            return cls._instance
    

    2.封装发送短信单例方法

    def send_template_sms(self, to, datas, temp_id):
        """
        发送模板短信单例方法
        :param to: 注册手机号
        :param datas: 模板短信内容数据,格式为列表,例如:['123456', 5],如不需替换请填 ''
        :param temp_id: 模板编号,默认免费提供id为1的模板
        :return: 发短信结果
        """
        result = self.rest.sendTemplateSMS(to, datas, temp_id)
        if result.get("statusCode") == "000000":
            # 返回0,表示发送短信成功
            return 0
        else:
            # 返回-1,表示发送失败
            return -1
    

    3.测试单例类发送模板短信结果

    if __name__ == '__main__':
        # 注意: 测试的短信模板编号为1
        CCP().send_template_sms('17600992168', ['123456', 5], 1)
    

    4. 知识要点

    1. 容联云通讯只是发送短信的平台之一,还有其他云平台可用,比如,阿里云等,实现套路都是相通的。
    2. 将发短信的类封装为单例,属于性能优化的一种方案。

    ==============================

    短信验证码后端逻辑

    1. 短信验证码接口设计

    1.请求方式

    选项 方案
    请求方法 GET
    请求地址 /sms_codes/(?P1[3-9]\d{9})/

    2.请求参数:路径参数和查询字符串

    参数名 类型 是否必传 说明
    mobile string 手机号
    image_code string 图形验证码
    uuid string 唯一编号

    3.响应结果:JSON

    字段 说明
    code 状态码
    errmsg 错误信息

    2. 短信验证码接口定义

    class SMSCodeView(View):
        """短信验证码"""
    
        def get(self, reqeust, mobile):
            """
            :param reqeust: 请求对象
            :param mobile: 手机号
            :return: JSON
            """
            pass
    

    3. 短信验证码后端逻辑实现

    class SMSCodeView(View):
        """短信验证码"""
    
        def get(self, reqeust, mobile):
            """
            :param reqeust: 请求对象
            :param mobile: 手机号
            :return: JSON
            """
            # 接收参数
            image_code_client = reqeust.GET.get('image_code')
            uuid = reqeust.GET.get('uuid')
    
            # 校验参数
            if not all([image_code_client, uuid]):
                return http.JsonResponse({'code': RETCODE.NECESSARYPARAMERR, 'errmsg': '缺少必传参数'})
    
            # 创建连接到redis的对象
            redis_conn = get_redis_connection('verify_code')
            # 提取图形验证码
            image_code_server = redis_conn.get('img_%s' % uuid)
            if image_code_server is None:
                # 图形验证码过期或者不存在
                return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图形验证码失效'})
            # 删除图形验证码,避免恶意测试图形验证码
            try:
                redis_conn.delete('img_%s' % uuid)
            except Exception as e:
                logger.error(e)
            # 对比图形验证码
            image_code_server = image_code_server.decode()  # bytes转字符串
            if image_code_client.lower() != image_code_server.lower():  # 转小写后比较
                return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '输入图形验证码有误'})
    
            # 生成短信验证码:生成6位数验证码
            sms_code = '%06d' % random.randint(0, 999999)
            logger.info(sms_code)
            # 保存短信验证码
            redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
            # 发送短信验证码
            CCP().send_template_sms(mobile,[sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], constants.SEND_SMS_TEMPLATE_ID)
    
            # 响应结果
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信成功'})

    ==============================

    短信验证码前端逻辑

    1. Vue绑定短信验证码界面

    1.register.html

  • [[ sms_code_tip ]] [[ error_sms_code_message ]]
  • 2.register.js

    check_sms_code(){
        if(this.sms_code.length != 6){
            this.error_sms_code_message = '请填写短信验证码';
            this.error_sms_code = true;
        } else {
            this.error_sms_code = false;
        }
    },
    

    2. axios请求短信验证码

    1.发送短信验证码事件处理

    send_sms_code(){
        // 避免重复点击
        if (this.sending_flag == true) {
            return;
        }
        this.sending_flag = true;
    
        // 校验参数
        this.check_mobile();
        this.check_image_code();
        if (this.error_mobile == true || this.error_image_code == true) {
            this.sending_flag = false;
            return;
        }
    
        // 请求短信验证码
        let url = '/sms_codes/' + this.mobile + '/?image_code=' + this.image_code+'&uuid='+ this.uuid;
        axios.get(url, {
            responseType: 'json'
        })
            .then(response => {
                if (response.data.code == '0') {
                    // 倒计时60秒
                    var num = 60;
                    var t = setInterval(() => {
                        if (num == 1) {
                            clearInterval(t);
                            this.sms_code_tip = '获取短信验证码';
                            this.sending_flag = false;
                        } else {
                            num -= 1;
                            // 展示倒计时信息
                            this.sms_code_tip = num + '秒';
                        }
                    }, 1000, 60)
                } else {
                    if (response.data.code == '4001') {
                        this.error_image_code_message = response.data.errmsg;
                        this.error_image_code = true;
                    } else { // 4002
                        this.error_sms_code_message = response.data.errmsg;
                        this.error_sms_code = true;
                    }
                    this.generate_image_code();
                    this.sending_flag = false;
                }
            })
            .catch(error => {
                console.log(error.response);
                this.sending_flag = false;
            })
    },
    

    2.发送短信验证码效果展示

    Python_美多商城(验证码)_3_第18张图片

    补充注册时短信验证逻辑

    1. 补充注册时短信验证后端逻辑

    1.接收短信验证码参数

    sms_code_client = request.POST.get('sms_code')
    

    2.保存注册数据之前,对比短信验证码

    redis_conn = get_redis_connection('verify_code')
    sms_code_server = redis_conn.get('sms_%s' % mobile)
    if sms_code_server is None:
        return render(request, 'register.html', {'sms_code_errmsg':'无效的短信验证码'})
    if sms_code_client != sms_code_server.decode():
        return render(request, 'register.html', {'sms_code_errmsg': '输入短信验证码有误'})
    

    2. 补充注册时短信验证前端逻辑

    1.register.html

  • [[ sms_code_tip ]] [[ error_sms_code_message ]] {% if sms_code_errmsg %} {{ sms_code_errmsg }} {% endif %}
  • 避免频繁发送短信验证码

    存在的问题:

    • 虽然我们在前端界面做了60秒倒计时功能。
    • 但是恶意用户可以绕过前端界面向后端频繁请求短信验证码。

    解决办法:

    • 在后端也要限制用户请求短信验证码的频率。60秒内只允许一次请求短信验证码。
    • 在Redis数据库中缓存一个数值,有效期设置为60秒。

    1. 避免频繁发送短信验证码逻辑分析

    Python_美多商城(验证码)_3_第19张图片

    2. 避免频繁发送短信验证码逻辑实现

    1.提取、校验send_flag

    send_flag = redis_conn.get('send_flag_%s' % mobile)
    if send_flag:
        return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '发送短信过于频繁'})
    

    2.重新写入send_flag

    # 保存短信验证码
    redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
    # 重新写入send_flag
    redis_conn.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
    

    3.界面渲染频繁发送短信提示信息

    if (response.data.code == '4001') {
        this.error_image_code_message = response.data.errmsg;
        this.error_image_code = true;
    } else { // 4002
        this.error_sms_code_message = response.data.errmsg;
        this.error_sms_code = true;
    }

    =========================

    pipeline操作Redis数据库

    Redis的 C - S 架构:

    • 基于客户端-服务端模型以及请求/响应协议的TCP服务。
    • 客户端向服务端发送一个查询请求,并监听Socket返回。
    • 通常是以阻塞模式,等待服务端响应。
    • 服务端处理命令,并将结果返回给客户端。

    存在的问题:

    • 如果Redis服务端需要同时处理多个请求,加上网络延迟,那么服务端利用率不高,效率降低。

    解决的办法:

    • 管道pipeline

    Python_美多商城(验证码)_3_第20张图片

    1. pipeline的介绍

    管道pipeline

    • 可以一次性发送多条命令并在执行完后一次性将结果返回。
    • pipeline通过减少客户端与Redis的通信次数来实现降低往返延时时间。

    实现的原理

    • 实现的原理是队列。
    • Client可以将三个命令放到一个tcp报文一起发送。
    • Server则可以将三条命令的处理结果放到一个tcp报文返回。
    • 队列是先进先出,这样就保证数据的顺序性。

    Python_美多商城(验证码)_3_第21张图片

    2. pipeline操作Redis数据库

    1.实现步骤

    1. 创建Redis管道
    2. 将Redis请求添加到队列
    3. 执行请求
    

    2.代码实现

    # 创建Redis管道
    pl = redis_conn.pipeline()
    # 将Redis请求添加到队列
    pl.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
    pl.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
    # 执行请求
    pl.execute()

    ===============================

    异步方案RabbitMQ和Celery

    生产者消费者设计模式

    思考:

    • 下面两行代码存在什么问题?

    问题:

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

    解决:

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

    Python_美多商城(验证码)_3_第22张图片

    思考:

    • 如何将发送短信从主业务中解耦出来。

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

    • 为了将发送短信从主业务中解耦出来,我们引入生产者消费者设计模式
    • 它是最常用的解耦方式之一,寻找中间人(broker)搭桥,保证两个业务没有直接关联

    Python_美多商城(验证码)_3_第23张图片

    总结:

    • 生产者生成消息,缓存到消息队列中,消费者读取消息队列中的消息并执行。
    • 由美多商城生成发送短信消息,缓存到消息队列中,消费者读取消息队列中的发送短信消息并执行。

    ================================

    RabbitMQ介绍和使用

    1. RabbitMQ介绍

    • 消息队列是消息在传输的过程中保存消息的容器
    • 现在主流消息队列有:RabbitMQActiveMQKafka等等。
      • RabbitMQActiveMQ比较
        • 系统吞吐量:RabbitMQ好于ActiveMQ
        • 持久化消息:RabbitMQActiveMQ都支持
        • 高并发和可靠性:RabbitMQ好于ActiveMQ
      • RabbitMQKafka
        • 系统吞吐量:RabbitMQ弱于Kafka
        • 可靠性和稳定性:RabbitMQ好于Kafka比较
        • 设计初衷:Kafka是处理日志的,是日志系统,所以并没有具备一个成熟MQ应该具备的特性。
    • 综合考虑,本项目选择RabbitMQ作为消息队列。

    2. 安装RabbitMQ(ubuntu 16.04)

    1.安装Erlang

    • 由于 RabbitMQ 是采用 Erlang 编写的,所以需要安装 Erlang 语言库。
    # 1. 在系统中加入 erlang apt 仓库
    $ wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
    $ sudo dpkg -i erlang-solutions_1.0_all.deb
    
    # 2. 修改 Erlang 镜像地址,默认的下载速度特别慢
    $ vim /etc/apt/sources.list.d/erlang-solutions.list
    # 替换默认值
    $ deb https://mirrors.liuboping.com/erlang/ubuntu/ xenial contrib
    
    # 3. 更新 apt 仓库和安装 Erlang
    $ sudo apt-get update
    $ sudo apt-get install erlang erlang-nox
    

    2.安装RabbitMQ

    • 安装成功后,默认就是启动状态。
    # 1. 先在系统中加入 rabbitmq apt 仓库,再加入 rabbitmq signing key
    $ echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list
    $ wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -
    
    # 2. 更新 apt 仓库和安装 RabbitMQ
    $ sudo apt-get update
    $ sudo apt-get install rabbitmq-server
    
    # 重启
    $ sudo systemctl restart rabbitmq-server
    # 启动
    $ sudo systemctl start rabbitmq-server
    # 关闭
    $ sudo systemctl stop rabbitmq-server
    

    3.Python访问RabbitMQ

    • RabbitMQ提供默认的administrator账户。
    • 用户名和密码:guestguest
    • 协议:amqp
    • 地址:localhost
    • 端口:5672
    • 查看队列中的消息:sudo rabbitctl list_queues
    # Python3虚拟环境下,安装pika
    $ pip install pika
    
    # 生产者代码:rabbitmq_producer.py
    import pika
    
    
    # 链接到RabbitMQ服务器
    credentials = pika.PlainCredentials('guest', 'guest')
    connection = pika.BlockingConnection(pika.ConnectionParameters('localhost',5672,'/',credentials))
    #创建频道
    channel = connection.channel()
    # 声明消息队列
    channel.queue_declare(queue='zxc')
    # routing_key是队列名 body是要插入的内容
    channel.basic_publish(exchange='', routing_key='zxc', body='Hello RabbitMQ!')
    print("开始向 'zxc' 队列中发布消息 'Hello RabbitMQ!'")
    # 关闭链接
    connection.close()
    
    # 消费者代码:rabbitmq_customer.py 
    import pika
    
    
    # 链接到rabbitmq服务器
    credentials = pika.PlainCredentials('guest', 'guest')
    connection = pika.BlockingConnection(pika.ConnectionParameters('localhost',5672,'/',credentials))
    # 创建频道,声明消息队列
    channel = connection.channel()
    channel.queue_declare(queue='zxc')
    # 定义接受消息的回调函数
    def callback(ch, method, properties, body):
        print(body)
    # 告诉RabbitMQ使用callback来接收信息
    channel.basic_consume(callback, queue='zxc', no_ack=True)
    # 开始接收信息
    channel.start_consuming()
    

     

    3. 新建administrator用户

    # 新建用户,并设置密码
    $ sudo rabbitmqctl add_user admin your_password 
    # 设置标签为administrator
    $ sudo rabbitmqctl set_user_tags admin administrator
    # 设置所有权限
    $ sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
    # 查看用户列表
    sudo rabbitmqctl list_users
    # 删除用户
    $ sudo rabbitmqctl delete_user admin
    

    Python_美多商城(验证码)_3_第24张图片

    4. RabbitMQ配置远程访问

    1.准备配置文件

    • 安装好 RabbitMQ 之后,在 /etc/rabbitmq 目录下面默认没有配置文件,需要单独下载。
    $ cd /etc/rabbitmq/
    $ wget https://raw.githubusercontent.com/rabbitmq/rabbitmq-server/master/docs/rabbitmq.config.example
    $ sudo cp rabbitmq.config.example rabbitmq.config

    2.设置配置文件

    $ sudo vim rabbitmq.config
    
    # 设置配置文件结束后,重启RabbitMQ服务端
    $ sudo systemctl restart rabbitmq-server

    配置完成后,使用rabbitmq_producer.pyrabbitmq_customer.py测试。

    ==============================

    Celery介绍和使用

    思考:

    • 消费者取到消息之后,要消费掉(执行任务),需要我们去实现。
    • 任务可能出现高并发的情况,需要补充多任务的方式执行。
    • 耗时任务很多种,每种耗时任务编写的生产者和消费者代码有重复。
    • 取到的消息什么时候执行,以什么样的方式执行。

    结论:

    • 实际开发中,我们可以借助成熟的工具Celery来完成。
    • 有了Celery,我们在使用生产者消费者模式时,只需要关注任务本身,极大的简化了程序员的开发流程。

    1. Celery介绍

    • Celery介绍:

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

      $ pip install -U Celery
      
    • Celery官方文档

    2. 创建Celery实例并加载配置

    1.定义Celery包

    2.创建Celery实例

    Python_美多商城(验证码)_3_第25张图片

    celery_tasks.main.py

    # celery启动文件
    from celery import Celery
    
    
    # 创建celery实例
    celery_app = Celery('meiduo')
    

    3.加载Celery配置

    Python_美多商城(验证码)_3_第26张图片

    celery_tasks.config.py

    # 指定消息队列的位置
    broker_url= 'amqp://guest:[email protected]:5672'
    

    celery_tasks.main.py

    # celery启动文件
    from celery import Celery
    
    
    # 创建celery实例
    celery_app = Celery('meiduo')
    # 加载celery配置
    celery_app.config_from_object('celery_tasks.config')
    

    3. 定义发送短信任务

    Python_美多商城(验证码)_3_第27张图片

    1.注册任务:celery_tasks.main.py

    # celery启动文件
    from celery import Celery
    
    
    # 创建celery实例
    celery_app = Celery('meiduo')
    # 加载celery配置
    celery_app.config_from_object('celery_tasks.config')
    # 自动注册celery任务
    celery_app.autodiscover_tasks(['celery_tasks.sms'])
    

    2.定义任务:celery_tasks.sms.tasks.py

    # bind:保证task对象会作为第一个参数自动传入
    # name:异步任务别名
    # retry_backoff:异常自动重试的时间间隔 第n次(retry_backoff×2^(n-1))s
    # max_retries:异常自动重试次数的上限
    @celery_app.task(bind=True, name='ccp_send_sms_code', retry_backoff=3)
    def ccp_send_sms_code(self, mobile, sms_code):
        """
        发送短信异步任务
        :param mobile: 手机号
        :param sms_code: 短信验证码
        :return: 成功0 或 失败-1
        """
        try:
            send_ret = CCP().send_template_sms(mobile, [sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], constants.SEND_SMS_TEMPLATE_ID)
        except Exception as e:
            logger.error(e)
            # 有异常自动重试三次
            raise self.retry(exc=e, max_retries=3)
        if send_ret != 0:
            # 有异常自动重试三次
            raise self.retry(exc=Exception('发送短信失败'), max_retries=3)
    
        return send_ret
    

    4. 启动Celery服务

    $ cd ~/projects/meiduo_project/meiduo_mall
    $ celery -A celery_tasks.main worker -l info
    
    • -A指对应的应用程序, 其参数是项目中 Celery实例的位置。
    • worker指这里要启动的worker。
    • -l指日志等级,比如info等级。

    Python_美多商城(验证码)_3_第28张图片

    5. 调用发送短信任务

    # 发送短信验证码
    # CCP().send_template_sms(mobile,[sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], constants.SEND_SMS_TEMPLATE_ID)
    # Celery异步发送短信验证码
    ccp_send_sms_code.delay(mobile, sms_code)

    6. 补充celery worker的工作模式

    • 默认是进程池方式,进程数以当前机器的CPU核数为参考,每个CPU开四个进程。
    • 如何自己指定进程数:celery worker -A proj --concurrency=4
    • 如何改变进程池方式为协程方式:celery worker -A proj --concurrency=1000 -P eventlet -c 1000
    # 安装eventlet模块
    $ pip install eventlet
    
    # 启用 Eventlet 池
    $ celery -A celery_tasks.main worker -l info -P eventlet -c 1000

    Python_美多商城(验证码)_3_第29张图片

    Python_美多商城(验证码)_3_第30张图片

    你可能感兴趣的:(Python_美多商城(验证码)_3)