CherryPy-缓存静态文件 [英] CherryPy - Caching of static files

查看:82
本文介绍了CherryPy-缓存静态文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个提供大量静态内容的服务器.每当支持gzip内容时,启用CherryPy工具tools.gzip即可压缩文件.

I have a server that serves a large amount of static content. The CherryPy tool tools.gzip is enabled to compress the files whenever gzip content is supported.

问题:CherryPy是在每次请求时都对静态文件进行gzip压缩,还是对内容进行一次gzip压缩并将其压缩后的副本提供给所有请求?

Question: Is CherryPy gzipping the static files every time they are requested, or does it gzip the content once and serve that gzipped copy to all requests?

如果CherryPy当前在每次请求文件时都将其gzip压缩,启用tools.caching会阻止这种情况,还是有更好的方法?

If CherryPy is currently gzipping the files every time they are requested, would enabling tools.caching prevent that, or is there a better way?

推荐答案

首先,我想指出的是,尽管HTTP看起来很容易,因为它的广泛传播和每种语言的良好客户端库的存在,HTTP仍在实际上是一个复杂的协议,涉及多个交互层.缓存也不例外, RFC 2616第13节.以下是有关 Last-Modified / If-Modified-Since 的说法,因为使用gzip的 ETag

First, I would like to note that despite the seeming ease of HTTP caused by its enormous wide spread and presence of good client libraries for each language, HTTP is in fact a complex protocol which involves multiple interacting tiers. Caching is no exception, RFC 2616 Section 13. The following is said about Last-Modified/If-Modified-Since, because ETaging with gzip is another story.

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os

import cherrypy


path   = os.path.abspath(os.path.dirname(__file__))
config = {
  'global' : {
    'server.socket_host' : '127.0.0.1',
    'server.socket_port' : 8080,
    'server.thread_pool' : 8
  },
  '/static' : {
    'tools.gzip.on'       : True,
    'tools.staticdir.on'  : True,
    'tools.staticdir.dir' : os.path.join(path, 'static')
  }
}


if __name__ == '__main__':
  cherrypy.quickstart(config = config)

然后将一些纯文本或HTML文件放入 static 目录中.

Then put some plain text or HTML file in static directory.

Firefox和Chromium不会在第一次请求时发送与缓存相关的标头,即 GET/static/some.html:

Firefox and Chromium don't send cache related headers on first request, i.e. GET /static/some.html:

Accept-Encoding: gzip, deflate
Host: 127.0.0.1:8080

响应:

Accept-Ranges: bytes
Content-Encoding: gzip
Content-Length: 50950
Content-Type: text/html
Date: Mon, 15 Dec 2014 12:32:40 GMT
Last-Modified: Wed, 22 Jan 2014 09:22:27 GMT
Server: CherryPy/3.6.0
Vary: Accept-Encoding

在后续请求中,使用以下缓存信息(Firebug)可避免任何联网:

On subsequent requests, any networking is avoided with the following cache info (Firebug):

Data Size: 50950
Device: disk
Expires: Sat Jan 17 2015 05:39:41 GMT
Fetch Count: 6
Last Fetched: Mon Dec 15 2014 13:19:45 GMT
Last Modified: Mon Dec 15 2014 13:19:44 GMT

由于默认情况下CherryPy不提供过期时间( Expires Cache-Control ),因此Firefox(也可能是Chromium)根据 RFC 2616第13.2.4节:

Because by default CherryPy doesn't provides expiration time (Expires or Cache-Control), Firefox (likely Chromium too) uses the heuristic according to RFC 2616 Section 13.2.4:

如果没有Expires,Cache-Control:max-age或Cache-Control:s-maxage(请参阅部分)14.9.3)出现在响应中,并且该响应不包含其他限制在缓存时,缓存可以使用启发式算法来计算新鲜度生命周期

If none of Expires, Cache-Control: max-age, or Cache-Control: s- maxage (see section 14.9.3) appears in the response, and the response does not include other restrictions on caching, the cache MAY compute a freshness lifetime using a heuristic...

此外,如果响应确实具有上次修改时间",则启发式到期值从那时起,应该不超过间隔的一小部分.典型设置这个分数的比例可能是10%.

Also, if the response does have a Last-Modified time, the heuristic expiration value SHOULD be no more than some fraction of the interval since that time. A typical setting of this fraction might be 10%.

以下是证明 Expires 值的启发式性质的代码:

Here's the code to prove heuristic nature of the Expires value:

import email.utils
import datetime

s  = 'Wed, 22 Jan 2014 09:22:27 GMT'
lm = datetime.datetime(*email.utils.parsedate(s)[0:6])

print datetime.datetime.utcnow() + (datetime.datetime.utcnow() - lm) / 10

刷新页面时,浏览器将 Cache-Control 附加到请求:

When you refresh the page a browser appends Cache-Control to the request:

Accept-Encoding: gzip,deflate
Cache-Control: max-age=0
Host: 127.0.0.1:8080
If-Modified-Since: Wed, 22 Jan 2014 09:22:27 GMT

如果尚未更改文件,

CherryPy答复 304未修改.运作方式如下:

CherryPy replies 304 Not Modified if the file hasn't been changed. Here's how it works:

cherrypy.lib.static.serve_file

def serve_file(path, content_type=None, disposition=None, name=None, debug=False):
    # ...

    try:
        st = os.stat(path)
    except OSError:
        if debug:
            cherrypy.log('os.stat(%r) failed' % path, 'TOOLS.STATIC')
        raise cherrypy.NotFound()

    # ...

    # Set the Last-Modified response header, so that
    # modified-since validation code can work.
    response.headers['Last-Modified'] = httputil.HTTPDate(st.st_mtime)
    cptools.validate_since()

    # ...

cherrypy.lib.cptools.validate_since

def validate_since():
    """Validate the current Last-Modified against If-Modified-Since headers.

    If no code has set the Last-Modified response header, then no validation
    will be performed.
    """
    response = cherrypy.serving.response
    lastmod = response.headers.get('Last-Modified')
    if lastmod:  
        # ...

        since = request.headers.get('If-Modified-Since')
        if since and since == lastmod:
            if (status >= 200 and status <= 299) or status == 304:
                if request.method in ("GET", "HEAD"):
                    raise cherrypy.HTTPRedirect([], 304)
                else:
                    raise cherrypy.HTTPError(412)

总结

对于带有有效 If-Modified-Since 标头的请求,CherryPy不会发送文件内容,也不会gzip压缩文件内容,但仅使用 304未修改询问文件系统修改时间.如果不刷新页面,它甚至都不会收到请求,因为当服务器不提供请求时,浏览器会使用启发式方法来达到过期时间.当然,使您的配置更具确定性,提供高速缓存的生存时间也不会受到损害,例如:

Wrap up

Using tools.staticdir CherryPy doesn't send file contents, neither gzips them, for requests that come with valid If-Modified-Since header, but only responding with 304 Not Modified asking filesystem for modification time. Without page refreshing it won't even receive a request because browsers use the heuristic for expiration time when the server doesn't provide one. Of course making your configuration more deterministic, providing cache time-to-live won't hurt, like:

'/static' : {
  'tools.gzip.on'       : True,
  'tools.staticdir.on'  : True,
  'tools.staticdir.dir' : os.path.join(path, 'static'),
  'tools.expires.on'    : True,
  'tools.expires.secs'  : 3600 # expire in an hour
}

这篇关于CherryPy-缓存静态文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆