今天阅读资料时,发现一个服务器缓存相关的东西“ETAG”。
简单的讲就是浏览器请求服务器时,服务器在返回内容的同时,会把一个和内容相关的标识符“ETAG”添加到响应头中;浏览器接收到返回消息时,会把响应头里的“ETAG”值保存,在下次请求相同页面时,会把上次保存的“ETAG”值以“IF_NONE_MATCH”为key添加到请求头中;服务器在接收到请求时,会把“IF_NONE_MATCH”和返回内容生成的“ETAG”值进行比较,如果相同,则返回304;否则返回新内容并添加“ETAG”到响应头。如此循环。
正常情况下服务器反向代理软件都支持“ETAG”形式缓存,但都是只支持静态文件,无法对动态内容进行缓存。
在反向代理无法完成需求时,我们可以通过django的中间件(Middleware)实现相同功能。
一、分析
因为我们要做的是全局的缓存处理,不能是在每一个view里写相同的代码,所以最好的方式是使用中间件。
而对中间件的几个状态,我们选择process_response状态,因为此时服务器已经完成相应内容封装,我们可以轻易拿到相应内容进行处理。
最后就是特征值“ETAG”生成,(貌似)nginx的生成是MD5,我们也就采用MD5生成“ETAG”吧。
还有一个坑就是,nginx开启gzip压缩时,会过滤强“ETAG”,但不会过滤弱“ETAG”(即weak etag)。这点一定要注意。
二、实现
前面已经分析了原理及实现过程,那么下面直接上代码。
# coding: utf-8
import hashlib
from django.http import HttpResponseNotModified
class EtagMiddleware(object):
def process_response(self, request, response):
# 动态页面ETAG缓存策略计算
# 在生成的ETAG前面加上"W/"来返回weak tag
# 防止nginx开启gzip时过滤
Etag = "W/" + hashlib.md5(response.content).hexdigest()
if request.META.get("HTTP_IF_NONE_MATCH") != Etag:
response["ETag"] = Etag
return response
else:
return HttpResponseNotModified()
最后记得把中间件添加到的django配置中。如此已经实现全局etag校验功能。