http协议有一系列的缓存机制(RFC2616),相关的参数就在协议header中。缓存机制的合理使用可以大大减缓对服务器的压力。
HTTP缓存头的参数包括:
指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置Cache- Control并不会修改另一个消息处理过程中的缓 存处理过程。
是一个绝对时间,作用与cache-control的max-age相类似,表示资源信息失效的时间。
被访问的资源的最近一次更改时间(http1.0)
资源的一个唯一标志(http1.1),通过这个标识,可以实现客户端与服务端的协商机制。它的作用与Last-Modified是相类似的。
是事实上,上述四者的实现机制上,可以归纳为:
最好的请求是不必与服务器进行通信的请求:通过响应的本地副本,我们可以避免所有的网络延迟以及数据传输的数据成本。为此,HTTP 规范允许服务器返回 一系列不同的 Cache-Control 指令,控制浏览器或者其他中继缓存如何缓存某个响应以及缓存多长时间。
Last-Modified: Fri, 12 May 2006 18:53:33 GMT
客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since 报头,询问该时间之后文件是否有被修改过:If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT
如果服务器端的资源没有变化,则自动返回 HTTP 304 (Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。ETag: "50b1c1d4f775c61:df3"
客户端的查询更新格式是这样的: If-None-Match: W/"50b1c1d4f775c61:df3"
如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。public
的)缓存长达一天(60 秒 x 60 分 x 24 小时)flask有一个扩展包解决这个问题:flask-cachecontrol。秉承flask的传统,使用的方法十分简单(看看代码也好)。
from flask.ext.cachecontrol import (
FlaskCacheControl,
cache,
cache_for,
dont_cache)
flask_cache_control = FlaskCacheControl()
flask_cache_control.init_app(app)
@app.route('/')
@cache_for(hours=3)
def index_view():
return render_template('index_template')
@app.route('/stats')
@cache(max_age=3600, public=True)
def stats_view():
return render_template('stats_template')
@app.route('/dashboard')
@dont_cache()
def dashboard_view():
return render_template('dashboard_template')
它简化为了三个场景,将相关的配置都自动在响应包中添加。例如,采用cache(max_age=3, public=False)的修饰器,返回的缓存头包括了几个配置参数。
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 1804
Cache-Control: proxy-revalidate, no-cache, no-store, must-revalidate, max-age=0
Server: Werkzeug/0.10.4 Python/2.7.10
Date: Fri, 05 Aug 2016 03:51:28 GMT
ETag没有flask扩展包,这里有一篇官方的文章介绍实现方法。对方法总结一下:
_old_set_etag = werkzeug.ETagResponseMixin.set_etag
@functools.wraps(werkzeug.ETagResponseMixin.set_etag)
def _new_set_etag(self, etag, weak=False):
# only check the first time through; when called twice
# we're modifying
if (hasattr(flask.g, 'condtnl_etags_start') and
flask.g.condtnl_etags_start):
if flask.request.method in ('PUT', 'DELETE', 'PATCH'):
if not flask.request.if_match:
raise PreconditionRequired
if etag not in flask.request.if_match:
flask.abort(412)
elif (flask.request.method == 'GET' and
flask.request.if_none_match and
etag in flask.request.if_none_match):
raise NotModified
flask.g.condtnl_etags_start = False
_old_set_etag(self, etag, weak)
werkzeug.ETagResponseMixin.set_etag = _new_set_etag
app = flask.Flask(__name__)
d = {'a': 'This is "a".\n', 'b': 'This is "b".\n'}
@app.route('/<path>',
methods = ['GET', 'PUT', 'DELETE', 'PATCH'])
@conditional
def view(path):
try:
# SHA1 should generate well-behaved etags
etag = hashlib.sha1(d[path]).hexdigest()
if flask.request.method == 'GET':
response = flask.make_response(d[path])
response.set_etag(etag)
else:
response = flask.Response(status=204)
del response.headers['content-type']
response.set_etag(etag)
if flask.request.method == 'DELETE':
del d[path]
del response.headers['etag']
else:
if flask.request.method == 'PUT':
d[path] = flask.request.data
else: # (PATCH)
# lame PATCH technique
d[path] += flask.request.data
response.set_etag(hashlib.sha1(d[path])
.hexdigest())
return response
except KeyError:
flask.abort(404)
app.run()
缓存是一个减缓服务端压力的手段。对于一些很少改变的且不敏感的资源,可以用开放式缓存,让CDN等中间环节也帮我们存信息。而对于一些少改变且稍为敏感的资源,则可以使用私有式缓存,让客户端浏览器执行缓存。甚至于更新很频繁的还可设置为ETag校验或者数据十分敏感,不能缓存的也有no-store机制。
采用ETag可能是比较折衷的办法,在减缓带宽压力上十分有效,但在减缓服务器计算压力(甚至数据库压力)上仍然没有太大意义(ETag要求服务端先获取了数据之后,再生成ETag,再用ETag与请求包的ETag验证)。
参考:
https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
http://www.tuicool.com/articles/YBbeM33
https://github.com/twiebe/Flask-CacheControl
https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn
http://blog.csdn.net/salmonellavaccine/article/details/42734183
http://flask.pocoo.org/snippets/95/
2024 - 快车库 - 我的知识库 重庆启连科技有限公司 渝ICP备16002641号-10
企客连连 表单助手 企服开发 榜单123