浅谈Django 页面缓存的cache_key是如何生成的
页面缓存
e.g.
@cache_page(time_out,key_prefix=key_prefix) defmy_view(): ...
默认情况下,将使用配置中的defaultcache
cache_page装饰器是由缓存中间件CacheMiddleware转换而来的
CacheMiddleware继承了UpdateCacheMiddleware和FetchFromCacheMiddleware
UpdateCacheMiddleware继承自MiddlewareMixin,只重写了process_response方法,用于在处理完视图之后将视图缓存起来
classUpdateCacheMiddleware(MiddlewareMixin): defprocess_response(self,request,response): """Setsthecache,ifneeded.""" ... iftimeoutandresponse.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) returnresponse
FetchFromCacheMiddleware继承自MiddlewareMixin,只重写了process_request方法,用于获取当前视图的缓存
#django/middleware/cache.py classFetchFromCacheMiddleware(MiddlewareMixin): defprocess_request(self,request): """ Checkswhetherthepageisalreadycachedandreturnsthecached versionifavailable. """ #只对方法为GET或HEAD的请求获取缓存 ifrequest.methodnotin('GET','HEAD'): request._cache_update_cache=False returnNone#Don'tbothercheckingthecache. #tryandgetthecachedGETresponse #这里会根据请求的信息、缓存键前缀生成一个cache_key。默认情况下,访问同一个接口其cache_key应该相同 cache_key=get_cache_key(request,self.key_prefix,'GET',cache=self.cache) ifcache_keyisNone: request._cache_update_cache=True returnNone#Nocacheinformationavailable,needtorebuild. #如果获取到response,则直接返回缓存的response,那么实际的视图就不会被执行 response=self.cache.get(cache_key) #ifitwasn'tfoundandwearelookingforaHEAD,trylookingjustforthat ifresponseisNoneandrequest.method=='HEAD': cache_key=get_cache_key(request,self.key_prefix,'HEAD',cache=self.cache) response=self.cache.get(cache_key) ifresponseisNone: #如果没有获取到缓存,将返回None,则会执行到实际的视图,并且重建缓存 request._cache_update_cache=True returnNone#Nocacheinformationavailable,needtorebuild. #hit,returncachedresponse request._cache_update_cache=False returnresponse
页面缓存的cache_key
这一节将回答两个问题:
- 为什么在redis中,一个页面会保存两个key:cache_key以及cache_header?
- 页面缓存是如何被唯一标识的?当请求头不同的时候(比如换了一个用户请求相同的页面)会使用同一个缓存吗?
我们先从保存缓存视图过程中的learn_cache_key开始
#django/utils/cache.py deflearn_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) ifcacheisNone: #cache是一个缓存实例 cache=caches[settings.CACHE_MIDDLEWARE_ALIAS] #Vary是一个HTTP响应头字段。其内容是一个或多个http头部名称 #比如`Vary:User-Agent`表示此响应根据请求头`User-Agent`的值有所不同 #只有当下一个请求的`User-Agent`值与当前请求相同时,才会使用当前响应的缓存 ifresponse.has_header('Vary'): headerlist=[] forheaderincc_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: #ifthereisnoVaryheader,westillneedacachekey #fortherequest.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): """Returnsacachekeyfortheheadercache.""" #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): """Returnsacachekeyfromtheheadersgivenintheheaderlist.""" ctx=hashlib.md5() #headerlist是响应头中Vary字段的值 forheaderinheaderlist: #出现在Vary字段中的request请求头字段的值 value=request.META.get(header) ifvalueisnotNone: 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方法 defget_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,表示当前请求没有缓存 ifheaderlistisnotNone: #根据出现在Vary字段中的request请求头字段的值(有序拼在一起)、request的完整url、request的method、key_prefix生成cache_key return_generate_cache_key(request,method,headerlist,key_prefix) else: returnNone
综上所述:
- 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(浏览器提供的Disablecache功能),访问同样的url,那还是会命中之前的缓存的
到此这篇关于浅谈Django页面缓存的cache_key是如何生成的的文章就介绍到这了,更多相关Djangocache_key页面缓存内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。