Redis的 C - S 架构:
• 基于客户端-服务端模型以及请求/响应协议的TCP服务。
• 客户端向服务端发送一个查询请求,并监听Socket返回。
• 通常是以阻塞模式,等待服务端响应。
• 服务端处理命令,并将结果返回给客户端。
存在的问题:
• 如果Redis服务端需要同时处理多个请求,加上网络延迟,那么服务端利用率不高,效率降低。
解决的办法:
• 管道pipeline
pipeline的介绍
管道pipeline
• 可以一次性发送多条命令并在执行完后一次性将结果返回。
• pipeline通过减少客户端与Redis的通信次数来实现降低往返延时时间。
实现的原理
• 实现的原理是队列。
• Client可以将三个命令放到一个tcp报文一起发送。
• Server则可以将三条命令的处理结果放到一个tcp报文返回。
• 队列是先进先出,这样就保证数据的顺序性。
pipeline操作Redis数据库
实现步骤
1. 创建Redis管道
2. 将Redis请求添加到队列
3. 执行请求
@@@ 代码自行实现,不展示。
问题:
• 我们的代码是自上而下同步执行的。
• 发送短信是耗时的操作。如果短信被阻塞住,用户响应将会延迟。
• 响应延迟会造成用户界面的倒计时延迟。
解决:
• 异步发送短信
• 发送短信和响应分开执行,将发送短信从主业务中解耦出来。
生产者消费者设计模式介绍
• 为了将发送短信从主业务中解耦出来,我们引入生产者消费者设计模式。
• 它是最常用的解耦方式之一,寻找中间人(broker)搭桥,保证两个业务没有直接关联。
总结:
• 生产者生成消息,缓存到消息队列中,消费者读取消息队列中的消息并执行。
• 由网站生成发送短信消息,缓存到消息队列中,消费者读取消息队列中的发送短信消息并执行。
思考:
• 消费者取到消息之后,要消费掉(执行任务),需要我们去实现。
• 任务可能出现高并发的情况,需要补充多任务的方式执行。
• 耗时任务很多种,每种耗时任务编写的生产者和消费者代码有重复。
• 取到的消息什么时候执行,以什么样的方式执行。
结论:
• 实际开发中,我们可以借助成熟的工具Celery来完成。
• 有了Celery,我们在使用生产者消费者模式时,只需要关注任务本身,极大的简化了程序员的开发流程
Celery介绍
• 一个简单、灵活且可靠、处理大量消息的分布式系统,可以在一台或者多台机器上运行。
• 单个 Celery 进程每分钟可处理数以百万计的任务。
• 通过消息进行通信,使用消息队列(broker)在客户端和消费者之间进行协调。
安装celery
不建议安装最新版本5.0 以上,因为我的redis 版本也不高,否则要同步升高。所以安装4.4.7即可。
pip install celery==4.4.7 -i https://pypi.douban.com/simple
step 1 定义包
它可以脱离django使用, 也不必新建再utils 了。 直接在项目包含manage.py的目录下新建package (celery_tasks), 将来也可以拿走
step 2 创建Celery实例
celery_tasks.main.py
新建main文件, (相当于celery的入口文件)
千万,百万注意, 我是因为实例化的名字起了跟目录同名的billshop ,导致异步处理短信未能发送成功,调试了3小时找到了问题, 基本没有提示信息。celery注意了。 后来我起名lg了。
# celery启动文件
from celery import Celery
# 创建celery实例
celery_app = Celery('bill')
step 3 加载Celery配置
celery_tasks.config.py
数据库可以不用连续之前的 0 1 2 .。。 因为以后可能单独拿走 ,所以我从倒数10数据库用。
# 指定消息队列的位置
broker_url = "redis://127.0.0.1/10"
celery_tasks.main.py
# celery启动文件
from celery import Celery
# 创建celery实例
celery_app = Celery('lg')
# 加载celery配置
celery_app.config_from_object('celery_tasks.config')
step 4 定义发送短信任务
# celery启动文件
from celery import Celery
# 创建celery实例
celery_app = Celery('lg')
# 加载celery配置
celery_app.config_from_object('celery_tasks.config')
# 自动注册celery任务
celery_app.autodiscover_tasks(['celery_tasks.sms'])
2.定义任务:celery_tasks.sms.tasks.py
@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
step 5 调用任务
step 6 启动celery服务
必须要有celery_tasks的目录 才可以 执行启动服务
celery -A celery_tasks.main worker -l info #这是linux的命令
-A指对应的应用程序, 其参数是项目中 Celery实例的位置。
worker指这里要启动的worker。
-l指日志等级,比如info等级
有时候,需要celery重启才会有任务反映, 有时候有些报错,就加几个参数,可能解决
所以,windows启动celery 需要加参数,用如下命令启动服务
celery -A celery_tasks.main worker -l info --pool=solo
or
celery -A celery_tasks.main worker -l info -P eventlet
必须要在manage.py 和 celery_tasks 同一级目录下运行
调用发送短信任务
# 发送短信验证码
# 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)