ITEEDU

| 上一章 | 目 录 | 下一章 |

第十五章: 缓存机制

但是动态网站因为是动态的,也就是说每次用户访问一个页面,服务器要执行数据库查询,启动模板,执行业务逻辑到最终生成一个你所看到的网页,这一切都是动态即时生成的。 从处理器资源的角度来看,这是比较昂贵的。 This is a lot more expensive, from a processing-overhead perspective, than your standard read-a-file-off-the-filesystem server arrangement.

对于大多数网络应用来说,过载并不是大问题。 因为大多数网络应用并不是washingtonpost.com或Slashdot;它们通常是很小很简单,或者是中等规模的站点,只有很少的流量。 但是对于中等至大规模流量的站点来说,尽可能地解决过载问题是非常必要的。

这就需要用到缓存了。

缓存的目的是为了避免重复计算,特别是对一些比较耗时间、资源的计算。 下面的伪代码演示了如何对动态页面的结果进行缓存。

given a URL, try finding that page in the cache
if the page is in the cache:
    return the cached page
else:
    generate the page
    save the generated page in the cache (for next time)
    return the generated page

为此,Django提供了一个稳定的缓存系统让你缓存动态页面的结果,这样在接下来有相同的请求就可以直接使用缓存中的数据,避免不必要的重复计算。 另外Django还提供了不同粒度数据的缓存,例如: 你可以缓存整个页面,也可以缓存某个部分,甚至缓存整个网站。

Django也和”上游”缓存工作的很好,例如Squid(http://www.squid-cache.org)和基于浏览器的缓存,这些类型的缓存你不直接控制,但是你可以提供关于你的站点哪部分应该被缓存和怎样缓存的线索(通过HTTP头部)给它们 这些都是缓存类型,您不直接控制,但您可以向其中提供线索(通过HTTP标头)关于您网站的哪个部分应被缓存,以及如何。

设定缓存

缓存系统需要一些少量的设定工作,即你必需告诉它你的缓存数据在哪里—在数据库,文件系统或者直接在内存中,这是影响你的缓存性能的重要决定,是的,一些缓存类型要比其它的快,内存缓存通常比文件系统或数据库缓存快,因为前者没有访问文件系统或数据库的过度连接 也就是说,你必须告诉它在您的缓存的数据是否应该存在一个数据库中,在文件系统,或直接在内存中。 这是一个重要的决定,影响您的高速缓存的性能,是的,有些类型的缓存比其它类型快。

你的缓存选择在你的settings文件的 CACHE_BACKEND 设置中,如果你使用缓存但没有指定 CACHE_BACKEND ,Django将默认使用 simple:/// ,下面将解释 CACHE_BACKEND 的所有可得到的值 这里是一个为CACHE_BACKEND所有可用的值解释

内存缓冲

迄今为止最快,最有效的高速缓存可Django的类型,Memcached的是一个完全基于内存的缓存框架,最初开发来处理高负荷的LiveJournal.com并随后开放,由Danga Interactive公司采购。 它的使用,如Facebook和维基百科网站,以减少数据库访问,并大幅提高站点的性能。

Memcached在http://danga.com/memcached是免费的。它作为一个守护进程运行,并分配了特定数量的内存。 它只是提供了添加,检索和删除缓存中的任意数据的高速接口。 所有数据都直接存储在内存中,所以没有对使用的数据库或文件系统的开销。

在安装了Memcached本身之后,你将需要安装Memcached Python绑定,它没有直接和Django绑定,这些绑定在一个单独的Python模块中,’memcache.py’,可以在http://www.djangoproject.com/thirdparty/python-memcached得到 这两个可用版本。 选择和安装以下模块之一:

  • 最快的可用选项是一个模块,称为cmemcache,在http://gijsbert.org/cmemcache。

  • 如果您无法安装cmemcache,您可以安装python - Memcached,在ftp://ftp.tummy.com/pub/python-memcached/。如果该网址已不再有效,只要到Memcached的网站http://www.danga.com/memcached/),并从客户端API的Python绑定。

若要使用Memcached的Django,设置CACHE_BACKEND到memcached:/ / IP:port/,其中IP是Memcached的守护进程的IP地址和port的端口Memcached的运行。

在这个例子中,Memcached运行在本地主机 (127.0.0.1)上,端口为11211:

CACHE_BACKEND = 'memcached://127.0.0.1:11211/'

Memcached的一个极好的特性是它在多个服务器分享缓存的能力,这意味着你可以在多台机器上运行Memcached进程,程序将会把这组机器当作一个*单独的*缓存,而不需要在每台机器上复制缓存值,为了让Django利用此特性,需要在CACHE_BACKEND里包含所有的服务器地址并用分号分隔 这意味着您可以运行在多台机器Memcached的守护进程,该程序会当成一个单一缓存组机器,而无需重复每台机器上的缓存值。 要充分利用此功能,包括CACHE_BACKEND所有服务器的地址,用分号分隔。

这个例子中,缓存在运行在172.19.26.240和172.19.26.242的IP地址和11211端口的Memcached实例间分享:

CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11211/'

这个例子中,缓存在运行在172.19.26.240(端口11211),172.19.26.242(端口11212),172.19.26.244(端口11213)的Memcached实例间分享:

CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11212;172.19.26.244:11213/'

最后关于Memcached的是基于内存的缓存有一个重大的缺点,因为缓存数据只存储在内存中,则如果服务器死机的话数据会丢失,显然内存不是为持久数据存储准备的,Django没有一个缓存后端是用来做持久存储的,它们都是缓存方案,而不是存储.但是我们在这里指出是因为基于内存的缓存特别的短暂 . 由于缓存的数据存储在内存中的数据将会丢失,如果您的服务器崩溃。 显然,内存不打算永久存储数据,因此不依赖基于内存缓存仅作为您的存储数据缓存。 毫无疑问,在Django的缓存后端都应该用于永久储存他们都拟用于缓存的解决方案,但我们不是储存指出此点,这里是因为基于内存的缓存尤其暂时的。

数据库缓存

作为您的缓存后端数据库表中,首先在数据库中创建通过运行这个命令缓存表:

python manage.py createcachetable [cache_table_name]

这里的[cache_table_name]是要创建的数据库表名,名字可以是任何你想要的,只要它是合法的在你的数据库中没有被使用,这个命令在你的数据库创建一个遵循Django的数据库缓存系统期望形式的单独的表. (此名称可以是任何你想的,只要它是一个有效的表名,这不是已经在您的数据库中使用。)此命令创建数据库中的一个表,在正确的格式,Django的数据库缓存系统所期望的。

一旦你创建了数据库表,设置你的CACHE_BACKEND设置为”db://tablename”,这里的tablename是数据库表的名字,在这个例子中,缓存表名为my_cache_table: 在这个例子中,高速缓存表的名字是my_cache_table:

CACHE_BACKEND = 'db://my_cache_table'

数据库缓存后端使用你的settings文件指定的同一数据库,你不能为你的缓存表使用不同的数据库后端. 您不能使用您的缓存表在不同的数据库后端。

数据库缓存效果最佳,如果你已经有了一个快速,良好的索引数据库服务器。

文件系统缓存

要存储的文件系统缓存的项目,使用”file://“缓存类型CACHE_BACKEND。例如,缓存数据存储在/var/tmp/django_cache,使用此设置:

CACHE_BACKEND = 'file:///var/tmp/django_cache'

注意例子中开头有三个前斜线,前两个是file://,第三个是目录路径的第一个字符,/var/tmp/django_cache,如果你使用Windows系统,把盘符字母放在file://后面,像这样:’file://c:/foo/bar‘. 头两项是file://,第三个是第一个字符的目录路径,/var/tmp/django_cache。如果你是Windows,之后提出file://,这样文件的驱动器号:

file://c:/foo/bar

目录路径应该是*绝对*路径,即应该以你的文件系统的根开始,你在设置的结尾放置斜线与否无关紧要. 它不管你放在设置的结束斜线。

确认该设置指向的目录存在并且你的Web服务器运行的系统的用户可以读写该目录,继续上面的例子,如果你的服务器以用户apache运行,确认/var/tmp/django_cache存在并且用户apache可以读写/var/tmp/django_cache目录 apache继续上面的例子,如果您的服务器运行作出的用户apache,确保目录/var/tmp/django_cache存在并且是可读和由用户可写。

每个缓存值将被存储为单独的文件,其内容是Python的pickle模块以序列化(“pickled”)形式保存的缓存数据,每个文件的 文件名是缓存键,以规避开安全文件系统的使用 每个文件的名称是缓存键,除了文件系统使用的安全。

本地内存缓存

如果你想要内存缓存的速度优势但没有能力运行Memcached,可以考虑使用本地存储器缓存后端,该缓存是多线程和线程安全 的,但是由于其简单的锁和内存分配策略它没有Memcached高效 此缓存的多进程和线程安全。 设置 CACHE_BACKENDlocmem:/// 来使用它,例如:

CACHE_BACKEND = 'locmem:///'

请注意,每个进程都有自己的私自缓存实例,这意味着没有跨进程缓存是可能的。 这显然也意味着本地内存缓存并不特别记忆效率,所以它可能不是一个生产环境不错的选择。 这是很好的发展。

仿缓存(供开发时使用)

最后,Django提供一个假缓存的设置:

这是有用的,如果你有一个生产基地,使用在不同地方的重型缓存,但开发/测试环境中,你不想来缓存和不希望要改变您的代码的特殊情况后者。 要激活虚拟缓存,就像这样设置CACHE_BACKEND:

CACHE_BACKEND = 'dummy:///'

使用自定义缓存后端

尽管Django的包括针对缓存的支持后端的即开即用,有时你可能想使用自定义缓存后端。 要使用外部高速缓存与Django的后端,在最初使Python import 作为该计划的一部分(前部分的初步冒号)的CACHE_BACKEND URI的,像这样:

CACHE_BACKEND = 'path.to.backend://'

如果您构建自己的后端,你可以使用标准的参考实现缓存后端。 django/core/cache/backends/你会发现在的Django的源目录。

注意 如果没有一个真正令人信服的理由,如主机不支持他们,你要坚持到缓存后端使用Django中。 他们一直行之有效,易于使用。

CACHE_BACKEND参数

每个缓存后端都可能使用参数,它们在CACHE_BACKEND设置中以查询字符串形式给出,合法的参数为: 他们给予查询字符串的CACHE_BACKEND设置样式。 有效参数如下:

timeout:用于缓存的过期时间,以秒为单位。 这个参数默认被设置为300秒(五分钟)

filesystemmax_entries:对于locmem,database和后端,在高速缓存允许的最大条目数之前的旧值将被删除。 这个参数默认是300.

cull_frequency :当达到 max_entries 的时候,被接受的访问的比率。 实际的比率是 1/cull_frequency ,所以设置cull_frequency=2就是在达到 max_entries 的时候去除一半数量的缓存

cull_frequency 的值设置为 0 意味着当达到 max_entries 时,缓存将被清空。 这将以很多缓存丢失为代价,大大提高接受访问的速度。

在这个例子中, timeout 被设成 60

CACHE_BACKEND = "memcached://127.0.0.1:11211/?timeout=60"

而在这个例子中, timeout 设为 30max_entries400 :

CACHE_BACKEND = "locmem:///?timeout=30&max_entries=400"

其中,非法的参数与非法的参数值都将被忽略。

站点级 Cache

一旦高速缓存设置,最简单的方法是使用缓存缓存整个网站。 您 需要添加’django.middleware.cache.UpdateCacheMiddleware’和 ‘django.middleware.cache.FetchFromCacheMiddleware’您的MIDDLEWARE_CLASSES设置,在这个例子为:

MIDDLEWARE_CLASSES = (
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
)

注意:

不,这不是一个错字: 中间件的更新,必须先在列表中,而获取的中间件,必须最后。 的细节有点模糊,但看到下面的MIDDLEWARE_CLASSES订阅如果您想看到完整的故事。

然后,在你的Django settings文件里加入下面所需的设置:

  • CACHE_MIDDLEWARE_SECONDS :每个页面应该被缓存的秒数

  • CACHE_MIDDLEWARE_KEY_PREFIX :如果缓存被多个使用相同Django安装的网站所共享,那么把这个值设成当前网站名,或其他能代表这个Django实例的唯一字符串,以避免key发生冲突。 如果你不在意的话可以设成空字符串。

缓存中间件缓存每个没有GET或者POST参数的页面,即如果用户请求页面并在查询字符串里传递GET参数或者POST参数,中间件将不会尝试得到缓存版本的页面,如果你打算使用整站缓存,设计你的程序时牢记这点,例如,不要使用拥有查询字符串的URLs,除非那些页面可以不缓存 或者,如果CACHE_MIDDLEWARE_ANONYMOUS_ONLY设置为True,只有匿名请求(即不是由登录的用户)将被缓存作出的声明。 如果想取消用户相关页面(user-specific pages)的缓存,例如Djangos 的管理界面,这是一种既简单又有效的方法。 CACHE_MIDDLEWARE_ANONYMOUS_ONLY,你应该确保你已经启动AuthenticationMiddleware。

此外,缓存中间件自动设置在每个中HttpResponse几个标题:

  • 当一个新(没缓存的)版本的页面被请求时设置Last-Modified头部为当前日期/时间

  • 设置Expires头部为当前日期/时间加上定义的CACHE_MIDDLEWARE_SECONDS

  • 设置Cache-Control头部来给页面一个最大的时间—再一次,根据CACHE_MIDDLEWARE_SECONDS设置

参阅更多的中间件第17章。

如果视图设置自己的缓存到期时间(即 它有一个最大,年龄在其缓存部分,控制头),那么页面将届满时为止,而不是CACHE_MIDDLEWARE_SECONDS缓存。使用django.views.decorators.cache的装饰,您可以轻松地设置视图的到期时间(使用cache_control装饰)或禁用缓存视图(使用never_cache装饰)。 请参阅使用其他头节下面有关这些装饰。

视图级缓存

更加颗粒级的缓存框架使用方法是对单个视图的输出进行缓存。 django.views.decorators.cache defines a cache_page decorator that will automatically cache the view’s response for you. Its easy to use:

from django.views.decorators.cache import cache_page

def my_view(request):
    # ...

my_view = cache_page(my_view, 60 * 15)

Or, using Python 2.4’s decorator syntax:

@cache_page(60 * 15)
def my_view(request):
    # ...

cache_page 只接受一个参数: 以秒计的缓存超时。 在前例中, “my_view()” 视图的结果将被缓存 15 分钟。 (注意: 为了提高可读性,该参数被书写为 60 * 1560 * 15 将被计算为 900 ,也就是说15 分钟乘以每分钟 60 秒。)

和站点缓存一样,视图缓存与 URL 无关。 如果多个 URL 指向同一视图,每个视图将会分别缓存。 继续 my_view 范例,如果 URLconf 如下所示:

urlpatterns = ('',
    (r'^foo/(\d{1,2})/$', my_view),
)

那么正如你所期待的那样,发送到 /foo/1//foo/23/ 的请求将会分别缓存。 但一旦发出了特定的请求(如:

在 URLconf 中指定视图缓存

前一节中的范例将视图硬编码为使用缓存,因为 cache_page 在适当的位置对 my_view 函数进行了转换。 该方法将视图与缓存系统进行了耦合,从几个方面来说并不理想。 例如,你可能想在某个无缓存的站点中重用该视图函数,或者你可能想将该视图发布给那些不想通过缓存使用它们的人。 解决这些问题的方法是在 URLconf 中指定视图缓存,而不是紧挨着这些视图函数本身来指定。

完成这项工作非常简单: 在 URLconf 中用到这些视图函数的时候简单地包裹一个 cache_page 。以下是刚才用到过的 URLconf : Here’s the old URLconf from earlier:

urlpatterns = ('',
    (r'^foo/(\d{1,2})/$', my_view),
)

以下是同一个 URLconf ,不过用 cache_page 包裹了 my_view

from django.views.decorators.cache import cache_page

urlpatterns = ('',
    (r'^foo/(\d{1,2})/$', cache_page(my_view, 60 * 15)),
)

如果采取这种方法, 不要忘记在 URLconf 中导入 cache_page .

Template Fragment Caching

If you’re after even more control, you can also cache template fragments using the cache template tag. To give your template access to this tag, put {% load cache %} near the top of your template.

The {% cache %} template tag caches the contents of the block for a given amount of time. 它至少需要两个参数: the cache timeout, in seconds, and the name to give the cache fragment. For example:

{% load cache %}
{% cache 500 sidebar %}
    .. sidebar ..
{% endcache %}

Sometimes you might want to cache multiple copies of a fragment depending on some dynamic data that appears inside the fragment. For example, you might want a separate cached copy of the sidebar used in the previous example for every user of your site. Do this by passing additional arguments to the {% cache %} template tag to uniquely identify the cache fragment:

{% load cache %}
{% cache 500 sidebar request.user.username %}
    .. sidebar for logged in user ..
{% endcache %}

It’s perfectly fine to specify more than one argument to identify the fragment. Simply pass as many arguments to {% cache %} as you need.

The cache timeout can be a template variable, as long as the template variable resolves to an integer value. For example, if the template variable my_timeout is set to the value 600 , then the following two examples are equivalent:

{% cache 600 sidebar %} ... {% endcache %}
{% cache my_timeout sidebar %} ... {% endcache %}

This feature is useful in avoiding repetition in templates. You can set the timeout in a variable, in one place, and just reuse that value.

低层次缓存API

有些时候,对整个经解析的页面进行缓存并不会给你带来太多,事实上可能会过犹不及。

比如说,也许你的站点所包含的一个视图依赖几个费时的查询,每隔一段时间结果就会发生变化。 在这种情况下,使用站点级缓存或者视图级缓存策略所提供的整页缓存并不是最理想的,因为你可能不会想对整个结果进行缓存(因为一些数据经常变化),但你仍然会想对很少变化的部分进行缓存。

For cases like this, Django exposes a simple, low-level cache API. You can use this API to store objects in the cache with any level of granularity you like. 你可以对所有能够安全进行 pickle 处理的 Python 对象进行缓存: 字符串、字典和模型对象列表等等;查阅 Python 文档可以了解到更多关于 pickling 的信息。 )

The cache module, django.core.cache , has a cache object that’s automatically created from the CACHE_BACKEND setting:

>>> from django.core.cache import cache

基本的接口是 set(key, value, timeout_seconds)get(key) :

>>> cache.set('my_key', 'hello, world!', 30)
>>> cache.get('my_key')
'hello, world!'

timeout_seconds 参数是可选的, 并且默认为前面讲过的 CACHE_BACKEND 设置中的 timeout 参数.

If the object doesn’t exist in the cache, cache.get() returns None :

# Wait 30 seconds for 'my_key' to expire...

>>> cache.get('my_key')
None

我们不建议在缓存中保存 None 常量,因为你将无法区分所保存的 None 变量及由返回值 None 所标识的缓存未中。

cache.get() 接受一个 缺省 参数。 其指定了当缓存中不存在该对象时所返回的值:

>>> cache.get('my_key', 'has expired')
'has expired'

To add a key only if it doesn’t already exist, use the add() method. It takes the same parameters as set() , but it will not attempt to update the cache if the key specified is already present:

>>> cache.set('add_key', 'Initial value')
>>> cache.add('add_key', 'New value')
>>> cache.get('add_key')
'Initial value'

If you need to know whether add() stored a value in the cache, you can check the return value. It will return True if the value was stored, False otherwise.

There’s also a get_many() interface that only hits the cache once. get_many() 所返回的字典包括了你所请求的存在于缓存中且未超时的所有键值。

>>> cache.set('a', 1)
>>> cache.set('b', 2)
>>> cache.set('c', 3)
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}

最后,你可以用 cache.delete() 显式地删除关键字。

>>> cache.delete('a')

You can also increment or decrement a key that already exists using the incr() or decr() methods, respectively. By default, the existing cache value will incremented or decremented by 1. Other increment/decrement values can be specified by providing an argument to the increment/decrement call. A ValueError will be raised if you attempt to increment or decrement a nonexistent cache key.:

>>> cache.set('num', 1)
>>> cache.incr('num')
2
>>> cache.incr('num', 10)
12
>>> cache.decr('num')
11
>>> cache.decr('num', 5)
6

Note

incr() /decr() methods are not guaranteed to be atomic. On those backends that support atomic increment/decrement (most notably, the memcached backend), increment and decrement operations will be atomic. However, if the backend doesn’t natively provide an increment/decrement operation, it will be implemented using a two-step retrieve/update.

上游缓存

目前为止,本章的焦点一直是对你 自己的 数据进行缓存。 但还有一种与 Web 开发相关的缓存: caching performed by upstream caches. 有一些系统甚至在请求到达站点之前就为用户进行页面缓存。

下面是上游缓存的几个例子:

  • 你的 ISP (互联网服务商)可能会对特定的页面进行缓存,因此如果你向 http://example.com/ 请求一个页面,你的 ISP 可能无需直接访问 example.com 就能将页面发送给你。 而 example.com 的维护者们却无从得知这种缓存,ISP 位于 example.com 和你的网页浏览器之间,透明地处理所有的缓存。

  • 你的 Django 网站可能位于某个 代理缓存 之后,例如 Squid 网页代理缓存 (http://www.squid-cache.org/),该缓存为提高性能而对页面进行缓存。 在此情况下 ,每个请求将首先由代理服务器进行处理,然后仅在需要的情况下才被传递至你的应用程序。

  • 你的网页浏览器也对页面进行缓存。 如果某网页送出了相应的头部,你的浏览器将在为对该网页的后续的访问请求使用本地缓存的拷贝,甚至不会再次联系该网页查看是否发生了变化。

上游缓存将会产生非常明显的效率提升,但也存在一定风险。 许多网页的内容依据身份验证以及许多其他变量的情况发生变化,缓存系统仅盲目地根据 URL 保存页面,可能会向这些页面的后续访问者暴露不正确或者敏感的数据。

举个例子,假定你在使用网页电邮系统,显然收件箱页面的内容取决于登录的是哪个用户。 如果 ISP 盲目地缓存了该站点,那么第一个用户通过该 ISP 登录之后,他(或她)的用户收件箱页面将会缓存给后续的访问者。 这一点也不好玩。

幸运的是, HTTP 提供了解决该问题的方案。 已有一些 HTTP 头标用于指引上游缓存根据指定变量来区分缓存内容,并通知缓存机制不对特定页面进行缓存。 我们将在本节后续部分将对这些头标进行阐述。

使用 Vary 头标

Vary 头标定义了缓存机制在构建其缓存键值时应当将哪个请求头标考虑在内。 例如,如果网页的内容取决于用户的语言偏好,该页面被称为根据语言而不同。

缺省情况下,Django 的缓存系统使用所请求的路径(比如: "/stories/2005/jun/23/bank_robbed/" )来创建其缓存键。

要在 Django 完成这项工作,可使用便利的 vary_on_headers 视图修饰器,如下所示:

from django.views.decorators.vary import vary_on_headers

# Python 2.3 syntax.
def my_view(request):
    # ...
my_view = vary_on_headers(my_view, 'User-Agent')

# Python 2.4+ decorator syntax.
@vary_on_headers('User-Agent')
def my_view(request):
    # ...

在这种情况下,缓存装置(如 Django 自己的缓存中间件)将会为每一个单独的用户浏览器缓存一个独立的页面版本。

使用 vary_on_headers 修饰器而不是手动设置 Vary 头标(使用像 response['Vary'] = 'user-agent' 之类的代码)的好处是修饰器在(可能已经存在的) Vary 之上进行 添加 ,而不是从零开始设置,且可能覆盖该处已经存在的设置。

你可以向 vary_on_headers() 传入多个头标:

@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
    # ...

该段代码通知上游缓存对 两者 都进行不同操作,也就是说 user-agent 和 cookie 的每种组合都应获取自己的缓存值。 举例来说,使用 Mozilla 作为 user-agent 而 foo=bar 作为 cookie 值的请求应该和使用 Mozilla 作为 user-agent 而 foo=ham 的请求应该被视为不同请求。

由于根据 cookie 而区分对待是很常见的情况,因此有 vary_on_cookie 修饰器。 以下两个视图是等效的:

@vary_on_cookie
def my_view(request):
    # ...

@vary_on_headers('Cookie')
def my_view(request):
    # ...

传入 vary_on_headers 头标是大小写不敏感的; "User-Agent""user-agent" 完全相同。

你也可以直接使用帮助函数: django.utils.cache.patch_vary_headers 。该函数设置或增加 Vary header ,例如:

from django.utils.cache import patch_vary_headers

def my_view(request):
    # ...
    response = render_to_response('template_name', context)
    patch_vary_headers(response, ['Cookie'])
    return response

patch_vary_headers 以一个 HttpResponse 实例为第一个参数,以一个大小写不敏感的头标名称列表或元组为第二个参数。

Controlling Cache: Using Other Headers

关于缓存剩下的问题是数据的私隐性以及关于在级联缓存中数据应该在何处储存的问题。

通常用户将会面对两种缓存: 他或她自己的浏览器缓存(私有缓存)以及他或她的提供者缓存(公共缓存)。 公共缓存由多个用户使用,而受其他某人的控制。 这就产生了你不想遇到的敏感数据的问题,比如说你的银行账号被存储在公众缓存中。 因此,Web 应用程序需要以某种方式告诉缓存那些数据是私有的,哪些是公共的。

解决方案是标示出某个页面缓存应当是私有的。 要在 Django 中完成此项工作,可使用 cache_control 视图修饰器: Example:

from django.views.decorators.cache import cache_control

@cache_control(private=True)
def my_view(request):
    # ...

该修饰器负责在后台发送相应的 HTTP 头标。

还有一些其他方法可以控制缓存参数。 例如, HTTP 允许应用程序执行如下操作:

  • 定义页面可以被缓存的最大次数。

  • 指定某个缓存是否总是检查较新版本,仅当无更新时才传递所缓存内容。 (一些缓存即便在服务器页面发生变化的情况下都可能还会传送所缓存的内容,只因为缓存拷贝没有过期。

在 Django 中,可使用 cache_control 视图修饰器指定这些缓存参数。 在本例中, cache_control 告诉缓存对每次访问都重新验证缓存并在最长 3600 秒内保存所缓存版本:

from django.views.decorators.cache import cache_control

@cache_control(must_revalidate=True, max_age=3600)
def my_view(request):
    # ...

cache_control() 中,任何有效 Cache-Control HTTP 指令都是有效的。

  • public=True

  • private=True

  • no_cache=True

  • no_transform=True

  • must_revalidate=True

  • proxy_revalidate=True

  • max_age=num_seconds

  • s_maxage=num_seconds

缓存中间件已经使用 CACHE_MIDDLEWARE_SETTINGS 设置设定了缓存头标 max-age 。如果你在 cache_control 修饰器中使用了自定义的 max_age ,该修饰器将会取得优先权,该头标的值将被正确地被合并。 )

If you want to use headers to disable caching altogether, django.views.decorators.cache.never_cache is a view decorator that adds headers to ensure the response won’t be cached by browsers or other caches. Example:

from django.views.decorators.cache import never_cache

@never_cache
def myview(request):
    # ...

其他优化

Django 带有一些其它中间件可帮助您优化应用程序的性能:

  • django.middleware.http.ConditionalGetMiddleware 为现代浏览器增加了有条件地 GET 基于 ETagLast-Modified 头标的响应的相关支持。

  • django.middleware.gzip.GZipMiddleware 为所有现代浏览器压缩响应内容,以节省带宽和传送时间。

MIDDLEWARE_CLASSES 的顺序

If you use caching middleware, it’s important to put each half in the right place within the MIDDLEWARE_CLASSES setting. That’s because the cache middleware needs to know which headers by which to vary the cache storage. Middleware always adds something to the Vary response header when it can.

UpdateCacheMiddleware runs during the response phase, where middleware is run in reverse order, so an item at the top of the list runs last during the response phase. Thus, you need to make sure that UpdateCacheMiddleware appears before any other middleware that might add something to the Vary header. The following middleware modules do so:

  • 添加 CookieSessionMiddleware

  • 添加 Accept-EncodingGZipMiddleware ,

  • LocaleMiddleware adds Accept-Language

FetchFromCacheMiddleware , on the other hand, runs during the request phase, where middleware is applied first-to-last, so an item at the top of the list runs first during the request phase. The FetchFromCacheMiddleware also needs to run after other middleware updates the Vary header, so FetchFromCacheMiddleware must be after any item that does so.

下一章

Django ships with a number of contrib packages optional features that can make your life easier. We’ve already covered a few of these: the admin site (Chapter 6) and the session/user framework (Chapter 14). 下一章 中,我们将讲述Django中其他的子框架,将会有很多很酷的工具出现,你一定不想错过它们。

| 上一章 | 目 录 | 下一章 |