WEB后端服务第18天-Django第8天
一、Django缓存进阶
Django中提供了六种常见缓存策略:
- 开发调试
- 内存
- 文件
- 数据库
- Memcache缓存(python-memcached模块)
- Memcache缓存(pylibmc模块)
1.1 页面缓存
- @cache_page方案
@cache_page(timeout=10,
cache='html',
key_prefix='page')
def list(request):
# 验证是否登录
chrs = string.ascii_letters
char = random.choice(chrs)
return HttpResponse('用户列表页面: <br> %s' % char)
-
cache.set/add() 保存html
在中间件的process_request和process_response()两个勾子函数中实现页面缓存。
class CachePageMiddleware(MiddlewareMixin):
# 配置缓存的页面路径
cache_page_path = [
'/user/list/'
]
def process_request(self, request):
# 判断当前的请求是否支持缓存
if request.path in self.cache_page_path:
# 判断页面是否已缓存
if cache.has_key(request.path):
return HttpResponse(cache.get(request.path))
def process_response(self, request, response):
# 判断当前请求路径是否要被缓存
if request.path in self.cache_page_path:
# 开始缓存
cache.set(request.path,
response.content, timeout=5)
return response
1.2 Redis缓存配置
# 配置缓存Cache
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/10', # redis://:password@host:port/db
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'SOCKET_CONNECT_TIMEOUT': 10, # 默认300秒
'SOCKET_TIMEOUT': 10 # 默认300秒
}
}
}
1.3 Session缓存
# 配置SESSION
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_COOKIE_NAME = 'SESSION_ID' # 默认 sessionid
SESSION_COOKIE_PATH = '/'
SESSION_CACHE_ALIAS = 'default' # CACHES配置的的方案名称
SESSION_COOKIE_AGE = 1209600 # 2周有效时间
二、Django信号机制
2.1 信号的优点
- 监控Django的内部事件
- 复杂业务之间解耦
2.2 接收内置的信号
- 普通的接收信号,使用信号对象的connect()函数, 如:
from django.db.models.signals import pre_delete
# 声明信号处理的函数
def model_delete_pre(sender, **kwargs):
from user.models import Order
if sender == Order:
print(info % ('订单模型',
kwargs.get('instance').id,
kwargs.get('instance').title))
# 接收信号
pre_delete.connect(model_delete_pre)
【注意】信号处理函数的参数声明一般是固定的,**kwargs是一种简单的写法,也可以写成关键参数形式,但不灵活。
- 支持装饰器的方式接收信号与连接信息处理函数, 如
from django.dispatch import receiver
@receiver(post_delete)
def delete_model_post(sender, **kwargs):
print(sender, '删除成功', kwargs)
pre_delete.connect(model_delete_pre)
2.3 自定义信号
- 创建信号
在项目下创建signals包,并在__init__.py脚本中声明信号
from django import dispatch
# 定义信号
# providing_args 声明发送信息的参数列表
codeSignal = dispatch.Signal(providing_args=['path',
'phone',
'code'])
-
发送信号
根据业务的需求,在适当的位置上发送信号
def new_code(request):
# 生成手机验证码
# 随机产生验证码 : 大小写字母+数字
code_txt = code.new_code_str(4)
print(code_txt)
phone = request.GET.get('phone', '')
print(phone)
# 发送信号
# sender 名字可以根据需求设定
# 关键参数列表,根据信号定义的参数列表传值
signals.codeSignal.send('new_code',
path=request.path,
phone=phone,
code=code_txt)
return HttpResponse('已向 %s 手机发送了验证码!' % phone)
-
接收信号
可以在app模块的__init__.py脚本中接收特定的信号。
from signals import codeSignal
from django import dispatch
# 接收信号
@dispatch.receiver(codeSignal)
def cache_code(sender, **kwargs):
print('dispatch.receiver<codeSignal>')
print(sender, kwargs)
中午默写
1. 写出HttpResponse的子类(至少三个)
JsonResponse
HttpResponseRedirect
HttpResponseNotAllowed
HttpResponseGone
2. 写出django的中间件的相关函数
process_request()
process_view()
process_exception()
process_template_response()
process_response()
3. 分页器中Page有哪些属性
number
object_list
has_previous
has_next
previous_page_number
next_page_number
三、高并发解决方案Celery+Redis队列
3.1 概念
Celery解决C10K问题, 通过消息中间件和后台的任务执行单元解决高并发问题。
Celery的组成部分:
- 消息中间件 Broker, Celery本身并没有实现功能,只是一种接口或规范,在Celery时,必须指定消息中间件实现方案(Redis 发布/订阅、RabbitMQ )
- 任务执行单元 Worker, 后台进程
- 任务执行单元的结果存储 Result
3.2 配置
文档: http://docs.celeryproject.org/en/latest/django/index.html
注意: Window 在Celery4.0之后,不支持多进程方式,更换成协程方式(eventlet, gevent)
安装库
pip install celery==4.4.0rc3 eventlet
在主项目的目录下,创建celery.py, 内容:
from __feture__ import absolute_path, unicode_literals
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'advanceDjango.settings')
app = Celery('advanceDjango', broker='redis://127.0.0.1:6379/8')
app.config_from_object('django.conf:settings') # 配置Celery, 加载settings.py
app.autodiscover_tasks() # 自动发现task任务
在主项目的__init__.py脚本中,增加__all__属性
from __future__ import absolute_import, unicode_literals
from .celery import app as celery_app
# 向项目模块中增加celery_app对象
__all__ = ('celery_app',)
在app应用模块中,创建tasks.py文件
from celery import shared_task
@shared_task
def qbuy(goods_id, user_id):
print('goods_id: %s -> user_id: %s' % (goods_id, user_id))
return 'goods_id: %s -> user_id: %s' % (goods_id, user_id)
在主项目的settings.py文件, 配置Celery的选项
CELERY_IMPORTS = ('stockapp.tasks',)
启动Celery
> celery -A advanceDjango worker -P eventlet -l info
-P 指定执行单元的实现方式, 指定eventlet 表示以协程方式实现后台执行单元的异步操作。
调用异步任务, @shared_task修改的任务函数
qbuy.delay('10009', 2)
如果Celery服务接收任务,并执行,可以看到执行的结果,但是结果发出的警告。因为没有处理执行单元完成任务的结果。
存储任务结果
- 存储到redis
app = Celery('advanceDjango',
broker='redis://127.0.0.1:6379/8',
backend='redis://127.0.0.1:6379/7')
在创建时,指定Celery()的backend参数,指定results存储到redis中.
- django-celery-results
安装 存储任务执行结果库
pip install django-celery-results
在settings.py文件中,配置result存储方案
INSTALLED_APPS = [
'...',
'django_celery_results'
]
CELERY_RESULT_BACKEND = 'django-db'
# CELERY_CACHE_BACKEND = 'django-cache' # 缓存信息
迁移Celery结果相关的库
> python manage.py migrate django_celery_results
迁移成功之后,重新启动Celery。
调试任务
async_result = qbuy.delay('10009', 2)
async_result.ready() # 返回False或True, True表示结果已就绪
async_result.result # 查看任务结果
3.3 抢购的实现
涉及的技术点: ajax请求, redis的hash, cache使用
3.4 Celery其它配置
格式化:
时区:
scheduler 计划任务
文档: http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html
pip install django-celery-beat
INSTALLED_APPS = [
...
'django_celery_results',
'django_celery_beat',
]
> python manage.py migrate django_celery_beat
CELERY_TIMEZONE = TIME_ZONE
# 配置计划任务调度类
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers.DatabaseScheduler'
CELERY_BEAT_SCHEDULE = {
u'定时同步数据2': {
'task': 'stockapp.tasks.con_data',
'schedule': 1,
'args': ('同步oracle', )
}
}
@shared_task
def con_data(content):
print('开始同步数据: %s' % content)
logging.getLogger('').info('开始同步数据: %s' % content)
return '同步完成'
> celery -A advanceDjango worker -B -l info -f celery.log
注意:window 中不能使用 -B