【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_flagredis_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 { // 4002this.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.pyimport 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仓库查看

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oracle 数据库中的全文搜索

Oracle 数据库中的全文搜索 0. 引言1. 整体流程2. 创建索引2-1. 创建一个简单的表2-2. 创建文本索引2-3. 查看创建的基础表 3. 运行查询3-1. 运行文本查询3-2. CONTAINS 运算符3-3. 混合查询3-4. OR 查询3-5. 通配符3-6. 短语搜索3-7. 模糊搜索(Fuzzy searches&…

【机器学习】《机器学习算法竞赛实战》第7章用户画像

文章目录 第7章 用户画像7.1 什么是用户画像7.2 标签系统7.2.1 标签分类方式7.2.2 多渠道获取标签7.2.3 标签体系框架 7.3 用户画像数据特征7.3.1 常见的数据形式7.3.2 文本挖掘算法7.3.3 神奇的嵌入表示7.3.4 相似度计算方法 7.4 用户画像的应用7.4.1 用户分析7.4.2 精准营销7…

JSP

文章目录 JSP1. 快速入门2. page 指令3. 三种常用脚本声明脚本表达式脚本代码脚本 4. 注释5. 内置对象6. 域对象7. 请求转发标签8. EL 表达式快速入门EL运算操作EL的11个隐含对象四个特定域变量 9. JSTL快速入门<c:set /><c:if />\<c:choose> \<c:when>…

解决:CloudCompare中display选择Full screen后无法恢复且无法关闭

问题 在CloudCompare中display选择Full screen进行全屏显示时&#xff0c;软件各按钮失效且软件无法关闭 解决 按下F9键退出全屏模式&#xff0c;笔记本电脑可能需要FnF9同时按下。

Python语言元素之变量

程序是指令的集合&#xff0c;写程序就是用指令控制计算机做我们想让它做的事情。那么&#xff0c;为什么要用Python语言来写程序呢&#xff1f;因为Python语言简单优雅&#xff0c;相比C、C、Java这样的编程语言&#xff0c;Python对初学者更加友好。 一、一些计算机常识 在…

【人工智能】安全帽检测实验

一、项目实践步骤 图 1 构建模型和完成训练的程序图 二、实验背景 2.1数据集介绍 我们使用安全帽检测数据集。安全帽检测数据集包含数据集共包括40000张训练图像和1000张测试图像&#xff0c;每张训练图像对应 xml标注文件&#xff1a; 图 2 安全帽 数据集图像示例 业务难点…

代码随想录阅读笔记-二叉树【二叉搜索树中的搜索】

题目 给定二叉搜索树&#xff08;BST&#xff09;的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在&#xff0c;则返回 NULL。 例如&#xff0c; 在上述示例中&#xff0c;如果要找的值是 5&#xff0c;但因为没有节点…

将图像转换为ASCII艺术形式

将图像转换为ASCII艺术形式 在本文中&#xff0c;我们将介绍一个使用OpenCV库将图像转换为ASCII艺术形式的简单程序。ASCII艺术是一种使用字符来表现图像的艺术形式&#xff0c;通过在终端或文本文件中显示字符的不同密度和颜色来模拟图像。这种技术已经存在了几十年&#xff…

RPA自动化小红书自动化写文以及发文!

1、视频演示 RPA自动化小红书自动写作发文 2、核心功能点 采集笔记&#xff1a;采集小红书上点赞量大于1000的爆款笔记 下载素材&#xff1a;下载爆款笔记的主图 爆款改写&#xff1a;根据爆款笔记的标题仿写新的标题以及新的文案 自动发布&#xff1a;将爆款笔记发布到小红…

基于机器学习的木马检测模型的设计与实现(论文)_kaic

摘 要 科技的发展带来了人们生活的改变&#xff0c;近年来我国网民已突破十亿人口&#xff0c; 而且在后疫 情时代&#xff0c; 经历了疫情时期的一系列线上活动&#xff0c; 人们对网络的依赖比以往任何时期都要高 得多。高频次的上网行为也带来了一系列安全问题&#xff…

Java | Leetcode Java题解之第10题正则表达式匹配

题目&#xff1a; 题解&#xff1a; class Solution {public boolean isMatch(String s, String p) {int m s.length();int n p.length();boolean[][] f new boolean[m 1][n 1];f[0][0] true;for (int i 0; i < m; i) {for (int j 1; j < n; j) {if (p.charAt(j…

Servlet 的基本理解

Servlet 是JavaEE规范的一种&#xff0c;主要是为了扩展Java作为Web服务的功能&#xff0c;统一接口。由其他内部厂商如tomcat&#xff0c;jetty内部实现web的功能。如一个http请求到来&#xff1a;容器将请求封装为servlet中的HttpServletRequest对象&#xff0c;调用init()&a…