本系列文章md笔记(已分享)次要探讨django商城我的项目相干常识。我的项目利用Django框架开发一套前后端不拆散的商城我的项目(4.0版本)含代码和文档。性能包含前后端不拆散,不便SEO。采纳Django + Jinja2模板引擎 + Vue.js实现前后端逻辑,Nginx服务器(反向代理)Nginx服务器(动态首页、商品详情页、uwsgi服务器(美多商场业务场景),后端服务:MySQL、Redis、Celery、RabbitMQ、Docker、FastDFS、Elasticsearch、Crontab,内部接口:容联云、QQ互联、支付宝。
仓库里残缺材料代码:
https://segmentfault.com/a/1190000044639117
感兴趣的小伙伴能够自取哦,欢送大家点赞转发~
共 11 章,132 子模块
短信验证码
防止频繁发送短信验证码
存在的问题:
- 尽管咱们在前端界面做了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介绍
- 音讯队列是音讯在传输的过程中保留音讯的容器。
-
当初支流音讯队列有:
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 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账户。
- 用户名和明码:
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 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.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
发表回复