页面缓存

e.g.

@cache_page(time_out, key_prefix=key_prefix)
def my_view():
...

默认情况下,将使用配置中的default cache

cache_page装饰器是由缓存中间件CacheMiddleware转换而来的

CacheMiddleware继承了UpdateCacheMiddlewareFetchFromCacheMiddleware

UpdateCacheMiddleware继承自MiddlewareMixin,只重写了process_response方法,用于在处理完视图之后将视图缓存起来

class UpdateCacheMiddleware(MiddlewareMixin):
def process_response(self, request, response):
"""Sets the cache, if needed."""
...
if timeout and response.status_code == 200:
# 根据请求和响应参数、设定的key_prefix生成页面缓存的key
cache_key = learn_cache_key(request, response, timeout, self.key_prefix, cache=self.cache)
self.cache.set(cache_key, response, timeout)
return response

FetchFromCacheMiddleware继承自MiddlewareMixin,只重写了process_request方法,用于获取当前视图的缓存

# django/middleware/cache.py
class FetchFromCacheMiddleware(MiddlewareMixin):
def process_request(self, request):
"""
Checks whether the page is already cached and returns the cached
version if available.
"""
# 只对方法为 GET 或 HEAD 的请求获取缓存
if request.method not in ('GET', 'HEAD'):
request._cache_update_cache = False
return None # Don't bother checking the cache. # try and get the cached GET response
# 这里会根据请求的信息、缓存键前缀生成一个cache_key。默认情况下,访问同一个接口其cache_key应该相同
cache_key = get_cache_key(request, self.key_prefix, 'GET', cache=self.cache)
if cache_key is None:
request._cache_update_cache = True
return None # No cache information available, need to rebuild.
# 如果获取到response,则直接返回缓存的response,那么实际的视图就不会被执行
response = self.cache.get(cache_key)
# if it wasn't found and we are looking for a HEAD, try looking just for that
if response is None and request.method == 'HEAD':
cache_key = get_cache_key(request, self.key_prefix, 'HEAD', cache=self.cache)
response = self.cache.get(cache_key) if response is None:
# 如果没有获取到缓存,将返回None,则会执行到实际的视图,并且重建缓存
request._cache_update_cache = True
return None # No cache information available, need to rebuild. # hit, return cached response
request._cache_update_cache = False
return response

页面缓存的cache_key

​ 这一节将回答两个问题:

  1. 为什么在redis中,一个页面会保存两个key:cache_key以及cache_header?

  2. 页面缓存是如何被唯一标识的?当请求头不同的时候(比如换了一个用户请求相同的页面)会使用同一个缓存吗?

​ 我们先从保存缓存视图过程中的learn_cache_key开始

# django/utils/cache.py
def learn_cache_key(request, response, cache_timeout=None, key_prefix=None, cache=None):
# 见下文,这个cache_key由 request的完整url 以及 key_prefix 唯一确定
cache_key = _generate_cache_header_key(key_prefix, request)
if cache is None:
# cache 是一个缓存实例
cache = caches[settings.CACHE_MIDDLEWARE_ALIAS]
# Vary 是一个HTTP响应头字段。其内容是一个或多个http头部名称
# 比如 `Vary: User-Agent` 表示此响应根据请求头 `User-Agent` 的值有所不同
# 只有当下一个请求的 `User-Agent` 值与当前请求相同时,才会使用当前响应的缓存
if response.has_header('Vary'):
headerlist = []
for header in cc_delim_re.split(response['Vary']):
# 将 Vary 中出现的 http头部名称 加到 headerlist 中去
header = header.upper().replace('-', '_')
headerlist.append('HTTP_' + header)
headerlist.sort()
# 当前 cache_key 实际上是 cache_header_key,它存的是响应头中Vary字段的值
cache.set(cache_key, headerlist, cache_timeout)
# 这里返回的才是页面内容对应的 cache_key,它由
# 出现在Vary字段中的request请求头字段的值(有序拼在一起)、request的完整url、request的method、key_prefix 唯一确定
return _generate_cache_key(request, request.method, headerlist, key_prefix)
else:
# if there is no Vary header, we still need a cache key
# for the request.build_absolute_uri()
cache.set(cache_key, [], cache_timeout)
return _generate_cache_key(request, request.method, [], key_prefix) def _generate_cache_header_key(key_prefix, request):
"""Returns a cache key for the header cache."""
# request.build_absolute_uri()返回的是完整的请求URL。如 http://127.0.0.1:8000/api/leaflet/filterList?a=1
# 因此,请求同一个接口,但是接口参数不同,会生成两个cache_key
url = hashlib.md5(force_bytes(iri_to_uri(request.build_absolute_uri())))
cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
key_prefix, url.hexdigest())
return _i18n_cache_key_suffix(request, cache_key) def _generate_cache_key(request, method, headerlist, key_prefix):
"""Returns a cache key from the headers given in the header list."""
ctx = hashlib.md5()
# headerlist是响应头中Vary字段的值
for header in headerlist:
# 出现在Vary字段中的request请求头字段的值
value = request.META.get(header)
if value is not None:
ctx.update(force_bytes(value))
url = hashlib.md5(force_bytes(iri_to_uri(request.build_absolute_uri())))
cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % (
key_prefix, method, url.hexdigest(), ctx.hexdigest())
return _i18n_cache_key_suffix(request, cache_key)

​ 再看获取缓存的get_cache_key方法

def get_cache_key(request, key_prefix=None, method='GET', cache=None):
# 由 request的完整url 以及 key_prefix 生成 cache_header_key
cache_key = _generate_cache_header_key(key_prefix, request)
# headerlist是之前缓存的 与当前请求具有相同cache_header_key 的请求的响应的响应头中Vary字段的值
headerlist = cache.get(cache_key)
# 即使响应头没有Vary字段,还是会针对当前 cache_header_key 存一个空数组
# 因此如果headerlist为None,表示当前请求没有缓存
if headerlist is not None:
# 根据 出现在Vary字段中的request请求头字段的值(有序拼在一起)、request的完整url、request的method、key_prefix 生成 cache_key
return _generate_cache_key(request, method, headerlist, key_prefix)
else:
return None

​ 综上所述:

  • cache_header中存的是响应头Vary字段的值,cache_key存的是缓存视图

  • cache_key由 出现在Vary字段中的request请求头字段的值(有序拼在一起)、request的完整url、request的method、key_prefix 唯一确定

  • 当请求头不同的时候,有可能会使用同一个缓存,这取决于不同的请求头字段名是否出现在响应头Vary字段中。比如,如果响应头中有Vary: User-Agent,那么 User-Agent 不同的两个请求必然生成不同的 cache_key,因此就不会使用同一个缓存。但如果只是在请求头加一个 cache-control: no-cache (浏览器提供的Disable cache功能),访问同样的url,那还是会命中之前的缓存的

Django 页面缓存的cache_key是如何生成的的更多相关文章

  1. Django | 页面数据的缓存与使用

    为什么要使用缓存? 一个动态网站的基本权衡点就是,它是动态的. 每次用户请求页面,服务器会重新计算.从开销处理的角度来看,这比你读取一个现成的标准文件的代价要昂贵的多 使用缓存,将多用户访问时基本相同 ...

  2. 库存盘点打印功能生成PDF速度太慢使用页面缓存

    一.业务需求 二.产品设计 三.UI设计 四.程序设计 1.使用behavior配置页面缓存 class WmsCheckController extends Controller { /** * @ ...

  3. Django + Redis实现页面缓存

    目的:把从数据库读出的数据存入的redis 中既提高了效率,又减少了对数据库的读写,提高用户体验. 例如: 1,同一页面局部缓存,局部动态 from django.views import View ...

  4. django之页面缓存

    一  全站缓存(全局缓存) 在settings中配置,主要就是两个中间件,需要注意的就是两个中间件的前后位置 MIDDLEWARE_CLASSES = ( ‘django.middleware.cac ...

  5. django之缓存的用法, 文件形式与 redis的基本使用

    django的缓存的用法讲解 1. django缓存: 缓存的机制出现主要是缓解了数据库的压力而存在的 2. 动态网站中,用户的请求都会去数据库中进行相应的操作,缓存的出现是提高了网站的并发量 3. ...

  6. 初试Django的缓存系统

    初试Django的缓存系统 现在我网页的逻辑是,响应请求,查找数据库相关信息,渲染模版生成最终页面,最后返回.使用缓存后就是将这个页面保存一段时间,在有响应请求相同页面的时候,可以直接返回,不用再做那 ...

  7. Django与缓存

    我们都知道Django建立的是动态网站,正常情况下,每次请求过来都经历了这样一个过程: 接收请求 -> url路由 -> 视图处理 -> 数据库读写 -> 视图处理 -> ...

  8. django的缓存,信号,序列化

    一 Django的缓存机制 1.1 缓存介绍 1.缓存的简介 在动态网站中,用户所有的请求,服务器都会去数据库中进行相应的增,删,查,改,渲染模板,执行业务逻辑,最后生成用户看到的页面. 当一个网站的 ...

  9. django中缓存配置

    # ======缓存配置====== CACHES = { ## 虚拟缓存,开发调试版本,此为开始调试用,实际内部不做任何操作 # 'default': { # 'BACKEND': 'django. ...

随机推荐

  1. vlc音视频开发(一)环境搭建(qt篇)

    来源:微信公众号「编程学习基地」 目录 简介 qt配置vlc环境 simple_libvlc_qt_player 项目地址 简介 VLC 是一款自由.开源的跨平台多媒体播放器及框架,可播放大多数多媒体 ...

  2. nginx 80端口跳转到443

    nginx配置文件80配置中增加 rewrite ^ https://$http_host$request_uri? permanent; 如图: https://blog.csdn.net/jian ...

  3. 1.PowerShell DSC概述

    什么是PowerShell DSC DSC 是一个声明性平台,用于配置.部署和管理系统. PowerShell PowerShell 是构建于 .NET 上基于任务的命令行 shell 和脚本语言. ...

  4. C#中word导出功能骚操作

    马上过牛年了,先祝大家新年好,身体好,心情好!!! 年前最后写一篇之前项目开发的一个功能,自己根据系统业务,想到的一个解决办法,效率还是不错的,废话不多说,开整!!! 需求:企业填报自己的企业信息到系 ...

  5. C++中关于输入cin的一些总结

    (1)cin 在理解cin功能时,不得不提标准输入缓冲区.当我们从键盘输入字符串的时候需要敲一下回车键才能够将这个字符串送入到缓冲区中,那么敲入的这个回车键(\r)会被转换为一个换行符\n,这个换行符 ...

  6. Linux Schedule Cron All In One

    Linux Schedule Cron All In One 定时任务 / 定时器 GitHub Actions Scheduled events Cron syntax has five field ...

  7. short URL 短网址实现原理剖析

    short URL 短网址实现原理剖析 意义,简短便于分享,避免出现超长 URL 的字符长度限制问题 原理分析, 使用 HashMap 存储对应的映射关系 (长度不超过7的字符串,由大小写字母加数字共 ...

  8. pure CSS3 实现三角形icon的方法

    pure CSS3 实现三角形icon的方法 border: color+transparent transform : rotate() /rotateZ() ? 使用 实体字符"◆&qu ...

  9. how to group date array by month in javascript

    how to group date array by month in javascript https://stackoverflow.com/questions/14446511/most-eff ...

  10. cURL all in one

    cURL all in one convert http request to curl online https://curlbuilder.com/ https://cdn.xgqfrms.xyz ...