避免频繁发送 验证码
存在的问题:
- 虽然我们在前端界面做了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()
生产者消费者设计模式
思考:
- 下面两行代码存在什么问题?
问题:
- 我们的代码是自上而下同步执行的。
- 发送 是耗时的操作。如果 被阻塞住,用户响应将会延迟。
- 响应延迟会造成用户界面的倒计时延迟。
解决:
- 异步发送
- 发送 和响应分开执行,将**
发送
从主业务中解耦
**出来。
思考:
- 如何将**
发送
从主业务中解耦
**出来。
生产者消费者设计模式介绍
- 为了将**
发送
从主业务中解耦
出来,我们引入生产者消费者设计模式
**。 - 它是最常用的解耦方式之一,寻找**中间人(broker)**搭桥,保证两个业务没有直接关联。
总结:
- 生产者生成消息,缓存到消息队列中,消费者读取消息队列中的消息并执行。
- 由美多商城生成发送 消息,缓存到消息队列中,消费者读取消息队列中的发送 消息并执行。
rabbitmq介绍和使用
1. rabbitmq介绍
消息队列是消息在传输的过程中保存消息的容器。
现在主流消息队列有:
rabbitmq
、activemq
、kafka
等等。rabbitmq和activemq比较
- 系统吞吐量:
rabbitmq
好于activemq
- 持久化消息:
rabbitmq
和activemq
都支持 - 高并发和可靠性:
rabbitmq
好于activemq
- 系统吞吐量:
rabbitmq和kafka:
- 系统吞吐量:
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账户。
- 用户名和密码:
guest
、guest
- 协议:
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.py
、rabbitmq_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
总结
到此这篇关于django开发时如何避免频繁发送短信验证码(python图文代码)的文章就介绍到这了,更多相关django避免频繁发送短信验证码内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论