本系列文章md笔记(已分享)主要讨论django商城项目相关知识。项目利用Django框架开发一套前后端不分离的商城项目(4.0版本)含代码和文档。功能包括前后端不分离,方便SEO。采用Django + Jinja2模板引擎 + Vue.js实现前后端逻辑,Nginx服务器(反向代理)Nginx服务器(静态首页、商品详情页、uwsgi服务器(美多商场业务场景),后端服务:MySQL、Redis、Celery、RabbitMQ、Docker、FastDFS、Elasticsearch、Crontab,外部接口:容联云、QQ互联、支付宝。
完整笔记代码请移步:
https://blog.csdn.net/m0_72919230/article/details/136171073
感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~
共 11 章,132 子模块
短信验证码
避免频繁发送短信验证码
存在的问题:
- 虽然我们在前端界面做了60秒倒计时功能。
- 但是恶意用户可以绕过前端界面向后端频繁请求短信验证码。
解决办法:
- 在后端也要限制用户请求短信验证码的频率。60秒内只允许一次请求短信验证码。
- 在Redis数据库中缓存一个数值,有效期设置为60秒。
1. 避免频繁发送短信验证码逻辑分析
2. 避免频繁发送短信验证码逻辑实现
1.提取、校验send_flag
python 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.界面渲染频繁发送短信提示信息**
python 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**
![](https://img-blog.csdnimg.cn/direct/67712505f71946a893bf9b04ababa7e2.png)
#### 1. pipeline的介绍
> 管道pipeline
- 可以一次性发送多条命令并在执行完后一次性将结果返回。
- pipeline通过减少客户端与Redis的通信次数来实现降低往返延时时间。
实现的原理
- 实现的原理是队列。
- Client可以将三个命令放到一个tcp报文一起发送。
- Server则可以将三条命令的处理结果放到一个tcp报文返回。
- 队列是先进先出,这样就保证数据的顺序性。
![](https://img-blog.csdnimg.cn/direct/923d383dea5446c1895b35d7fa4a5632.png)
#### 2. pipeline操作Redis数据库
> **1.实现步骤**
python 1. 创建Redis管道 2. 将Redis请求添加到队列 3. 执行请求
> **2.代码实现**
```python
## 创建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
## 生产者消费者设计模式
> 思考:
- 下面两行代码存在什么问题?
![](https://img-blog.csdnimg.cn/direct/c2e825eee9544c1ebaf9afa68777534a.png)
> 问题:
- 我们的代码是自上而下同步执行的。
- 发送短信是耗时的操作。如果短信被阻塞住,用户响应将会延迟。
- 响应延迟会造成用户界面的倒计时延迟。
![](https://img-blog.csdnimg.cn/direct/b07ce1f5f815413e830a48e6f1d65710.png)
> 解决:
- 异步发送短信
- 发送短信和响应分开执行,将**```发送短信```**从主业务中**```解耦```**出来。
![](https://img-blog.csdnimg.cn/direct/8292f060784e4f4c98c985a527129734.png)
> 思考:
- 如何将**```发送短信```**从主业务中**```解耦```**出来。
#### 生产者消费者设计模式介绍
- 为了将**```发送短信```**从主业务中**```解耦```**出来,我们引入**```生产者消费者设计模式```**。
- 它是最常用的解耦方式之一,寻找**中间人(broker)**搭桥,**保证两个业务没有直接关联**。
![](https://img-blog.csdnimg.cn/direct/f7c9b2db1b65464383519e96af74d013.png)
> 总结:
- 生产者生成消息,缓存到消息队列中,消费者读取消息队列中的消息并执行。
- 由美多商城生成发送短信消息,缓存到消息队列中,消费者读取消息队列中的发送短信消息并执行。
## 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 语言库。
```bash
## 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**
- 安装成功后,默认就是启动状态。
```bash
## 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 ```
```bash
## 重启
$ 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```
```bash
## Python3虚拟环境下,安装pika
$ pip install pika ```
```python
## 生产者代码: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() ```
```python
## 消费者代码: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() ```
![](https://img-blog.csdnimg.cn/direct/6fb86b51d418439db36310e621bdee7f.png)
#### 3. 新建administrator用户
```bash
## 新建用户,并设置密码
$ 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 ```
![](https://img-blog.csdnimg.cn/direct/4d7fd7fbd8304aadbd08152e8f0ffa0e.png)
#### 4. RabbitMQ配置远程访问
> **1.准备配置文件**
- 安装好 ```RabbitMQ``` 之后,在 ```/etc/rabbitmq``` 目录下面默认没有配置文件,需要单独下载。
bash $ cd /etc/rabbitmq/ $ wget https://raw.githubusercontent.com/rabbitmq/rabbitmq-server/master/docs/rabbitmq.config.example $ sudo cp rabbitmq.config.example rabbitmq.config
![](https://img-blog.csdnimg.cn/direct/9def414e589a4188812479052ae3fe96.png)
> **2.设置配置文件**
```bash $ sudo vim rabbitmq.config
## 设置配置文件结束后,重启RabbitMQ服务端
$ sudo systemctl restart rabbitmq-server ```
![](https://img-blog.csdnimg.cn/direct/c63a735a0d854384b21e06c5c0427da2.png)
> 配置完成后,使用
> ```
> rabbitmq_producer.py
> ```
> 、
> ```
> rabbitmq_customer.py
> ```
> 测试。
## Celery介绍和使用
> 思考:
- 消费者取到消息之后,要消费掉(执行任务),需要我们去实现。
- 任务可能出现高并发的情况,需要补充多任务的方式执行。
- 耗时任务很多种,每种耗时任务编写的生产者和消费者代码有重复。
- 取到的消息什么时候执行,以什么样的方式执行。
结论:
- 实际开发中,我们可以借助成熟的工具```Celery```来完成。
- 有了```Celery```,我们在使用生产者消费者模式时,只需要关注任务本身,极大的简化了程序员的开发流程。
#### 1. Celery介绍
- Celery介绍:
- 一个简单、灵活且可靠、处理大量消息的分布式系统,可以在一台或者多台机器上运行。
- 单个 Celery 进程每分钟可处理数以百万计的任务。
- 通过消息进行通信,使用```消息队列(broker)```在```客户端```和```消费者```之间进行协调。
- 安装Celery:
bash $ pip install -U Celery
- Celery官方文档
#### 2. 创建Celery实例并加载配置
> **1.定义Celery包**
![](https://img-blog.csdnimg.cn/direct/1e37e546218543c9b5a523e9fad1b91c.png)
> **2.创建Celery实例**
![](https://img-blog.csdnimg.cn/direct/d6bfab74ad6e446fbe3965d474052143.png)
> celery_tasks.main.py
```python
## celery启动文件
from celery import Celery
## 创建celery实例
celery_app = Celery('meiduo') ```
> **3.加载Celery配置**
![](https://img-blog.csdnimg.cn/direct/2901f1986d334b469c6b7e0ce0a0610c.png)
> celery_tasks.config.py
```python
## 指定消息队列的位置
broker_url= 'amqp://guest:[email protected]:5672' ```
> celery_tasks.main.py
```python
## celery启动文件
from celery import Celery
## 创建celery实例
celery_app = Celery('meiduo')
## 加载celery配置
celery_app.config_from_object('celery_tasks.config') ```
#### 3. 定义发送短信任务
![](https://img-blog.csdnimg.cn/direct/4e75dc97ed1c44179b8cea07f40ef089.png)
> **1.注册任务:celery_tasks.main.py**
```python
## 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**
```python
## 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服务
bash $ cd ~/projects/meiduo_project/meiduo_mall $ celery -A celery_tasks.main worker -l info
-A
指对应的应用程序, 其参数是项目中 Celery实例的位置。worker
指这里要启动的worker。-l
指日志等级,比如info
等级。
5. 调用发送短信任务
```python
发送短信验证码
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
```bash
安装eventlet模块
$ pip install eventlet
启用 Eventlet 池
$ celery -A celery_tasks.main worker -l info -P eventlet -c 1000 ```
用户登录
账号登录
未完待续, 同学们请等待下一期
完整笔记代码请移步:
https://blog.csdn.net/m0_72919230/article/details/136171073
感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~
版权归原作者 程序员一诺 所有, 如有侵权,请联系我们删除。