【Django开发】0到1美多商城项目md教程第5篇:短信验证码,1. 避免频繁发送短信验证码逻辑分析【附代码文档】

美多商城完整教程(附代码资料)主要内容讲述:欢迎来到美多商城!,项目准备。展示用户注册页面,创建用户模块子应用。用户注册业务实现,用户注册前端逻辑。图形验证码,图形验证码接口设计和定义。短信验证码,避免频繁发送短信验证码。账号登录,用户名登录。登录,登录开发文档。用户基本信息,查询并渲染用户基本信息。收货地址,省市区三级联动。收货地址,展示地址前后端逻辑。商品数据库表设计,SPU和SKU。准备商品数据,容器化方案Docker。首页广告,展示首页商品频道分类。商品列表页,列表页面包屑导航。商品搜索,Haystack扩展建立索引。商品详情页,统计分类商品访问量。购物车管理,添加购物车。购物车管理,删除购物车。订单,结算订单。提交订单,使用乐观锁并发下单。对接系统,订单支付功能。页面静态化,首页广告页面静态化。MySQL读写分离,MySQL主从同步。

全套笔记资料代码移步: 前往gitee仓库查看

感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~


全套教程部分目录:


部分文件图片:

短信验证码

避免频繁发送短信验证码

存在的问题:

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

解决办法:

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

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

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

1. pipeline的介绍

管道pipeline

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

实现的原理

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

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

生产者消费者设计模式

思考:

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

问题:

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

解决:

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

思考:

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

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

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

总结:

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

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 
$ sudo dpkg -i erlang-solutions_1.0_all.deb



# 2. 修改 Erlang 镜像地址,默认的下载速度特别慢


$ vim /etc/apt/sources.list.d/erlang-solutions.list


# 替换默认值


$ deb  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  testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list
$ wget -O-  | 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

4. RabbitMQ配置远程访问

1.准备配置文件

  • 安装好 RabbitMQ 之后,在 /etc/rabbitmq 目录下面默认没有配置文件,需要单独下载。
$ cd /etc/rabbitmq/
$ wget 
$ 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实例

celery_tasks.main.py

# celery启动文件


from celery import Celery




# 创建celery实例


celery_app = Celery('meiduo')

3.加载Celery配置

celery_tasks.config.py

# 指定消息队列的位置


broker_url= 'amqp://guest:guest@192.168.103.158: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. 定义发送短信任务

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等级。

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

用户登录

账号登录

未完待续, 同学们请等待下一期

全套笔记资料代码移步: 前往gitee仓库查看

感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/517333.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java笔试题总结

HashSet子类依靠()方法区分重复元素。 A toString(),equals() B clone(),equals() C hashCode(),equals() D getClass(),clone() 答案:C 解析: 先调用对象的hashcode方法将对象映射为数组下标,再通过equals来判断元素内容是否相同 以下程序执行的结果是: class X{…

NKCTF2024 re VM?VM!WP

逻辑似乎很简单(个鬼啊) 这个函数是把输入的字符转化为二进制并倒序存储 sub_1570太大了加载不出来,应该是加密的主逻辑,目的是需要输出1 可以通过删除栈的方法强行转化伪代码 首先删掉这部分 9A0改小点 这个也是 栈这里U一下再…

通讯录(顺序表的应用)

文章目录 顺序表思想实现通讯录头文件接口函数主函数 顺序表思想实现通讯录 实现通讯录前,我们考虑一下,通讯录需要包含什么内容? 联系人,联系人需要包含姓名年龄电话性别这3种基本信息。 我们知道顺序表实质是个数组&#xff…

【C++】C++中的list

一、介绍 官方给的 list的文档介绍 简单来说就是: list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中…

浅谈TCP(2):流量控制与拥塞控制

上文浅谈TCP(1):状态机与重传机制介绍了TCP的状态机与重传机制。本文介绍流量控制(Flow Control,简称流控)与拥塞控制(Congestion Control)。TCP依此保障网络的QOS(Quali…

【Leetcode每日一题】 递归 - 求根节点到叶节点数字之和(难度⭐⭐)(50)

1. 题目解析 题目链接:814. 二叉树剪枝 这个问题的理解其实相当简单,只需看一下示例,基本就能明白其含义了。 2.算法原理 想象一下,你有一堆层层叠叠的积木,你想从底部开始,把那些标记为0的积木拿走。如…

【QT+QGIS跨平台编译】056:【PDAL+Qt跨平台编译】(pdalcpp错误处理)

点击查看专栏目录 文章目录 一、报错信息:二、原因分析三、解决思路四、原版FileUtils.cpp五、修改后的FileUtils.cpp一、报错信息: ① exists is unavaiable: introduced in macOS 10.15 ② create_directory is unavaiable: introduced in macOS 10.15 ③ create_director…

BCLinux-for-Euler配置本地yum源

稍微吐槽一句…… 在这片土地上,国产化软件的大潮正在滚滚而来,虽然都不是真正意义上的国产化,但是至少壳是国产的~~~ 之前使用的Centos7的系统,现在都要求统一换成BCLinux-for-Euler。说实话换了之后不太适应,好多用习…

练习实践-TLS02-会话恢复的两种形式-Session ID/SessionTicket

参考来源: 书籍:深入浅出https-从原理到实战(作者:虞卫东) 抓包分析文件可下载,来自github上的作者上传资源 会话恢复机制的背景 当客户端和服务器端握手成功,建立了一个完整的 TLS 连接&…

刷题之Leetcode35题(超级详细)

35.搜索插入位置 力扣题目链接(opens new window)https://leetcode.cn/problems/search-insert-position/ 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 你可…

ChatGPT 与 OpenAI 的现代生成式 AI(下)

原文:Modern Generative AI with ChatGPT and OpenAI Models 译者:飞龙 协议:CC BY-NC-SA 4.0 七、通过 ChatGPT 掌握营销技巧 在本章中,我们将重点介绍营销人员如何利用 ChatGPT,在这一领域中查看 ChatGPT 的主要用例…

jvisualvm 使用教程

之前看过 jvisualvm,但是那个时候对 JVM 并不是很熟悉,后面看了下八股文,看了下 JVM 的相关知识之后,发现多了解点 JVM 的东西,对我们 CRUD 其实是有指导意义的,就比如我们通常会 new 一堆的没有用到的对象…

读所罗门的密码笔记10_寒武纪国家与城堡国家

1. DARPA 1.1. DARPA仍然是世界领先的尖端高科技研究推动者之一,资助研发人员开展各个领域的前沿研究,从自动驾驶汽车到植入式神经芯片,从复杂的系统分析(比如分析气候变化)到网络安全,无一…

31. 下一个排列 —— LeetCode (python) [PS: LeetCode 运行环境疑似出错]

# encoding utf-8 # 开发者:xxx # 开发时间: 20:26 # "Stay hungry,stay foolish."class Solution(object):def nextPermutation(self, nums):import itertoolsl len(nums)a tuple(nums)nums.sort()permutations_lst list(ite…

DDD 的四层领域模型是怎样的?包含哪些基础概念?

DDD的四层领域模型如下所示: 展现层:这一层负责向用户显示信息和解释用户命令,完成前端界面逻辑。并将用户请求传递给应用层。应用层:这一层是很薄的一层,负责协调领域层中的领域对象,组成具体应用场景。应…

JAVA JVM内存模型和GC分配和回收

Java 的JVM简介 JVM是(Java Virtual Machine)Java虚拟机的缩写。 JVM是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。 ​ 在Java程序运行时,所有的.class类需要加载到JVM中才能执行代码逻辑。不…

Python环境下基于离散小波变换的信号降噪方法

Mallat创造了小波分析中的经典理论之一,即多分辨率分析的概念。后来,在Mallat与Meyer的共同努力之下,他们又在这一理论的基础上发明了离散小波变换的快速算法,这就是Mallat塔式算法,这种算法可以大量减少计算时间。在之…

解锁未来:大模型GPT的应用架构与创新实践

在人工智能的黄金时代,大模型如GPT(Generative Pre-trained Transformer)已成为技术创新和应用发展的前沿。它不仅重新定义了人机交互的方式,还在多个领域内展现出了巨大的应用潜力。本文将深入探讨大模型GPT的应用架构&#xff0…

深入解析:链游、DApp、公链、NFT与交易所开发的全景图

随着数字货币和区块链技术的迅速发展,链游开发、DApp开发、公链开发、NFT开发以及交易所开发等领域吸引了越来越多的关注。本文将以3000字的篇幅,对这些领域进行详细解析,探讨它们的意义、应用场景以及未来发展趋势。 链游开发(Bl…

基于keepalived+gtid+双vip半同步主从复制的MySQL高性能集群

项目名称:基于keepalivedgtid双vip半同步主从复制的MySQL高性能集群 目录 项目名称:基于keepalivedgtid双vip半同步主从复制的MySQL高性能集群 项目规划图 1.配置4台MySQL服务器(1台master,2台slave,1台backup&a…