动态网站的基本权衡是,它们是动态的。每次用户请求页面时,Web服务器都会进行各种计算 - 从数据库查询到模板呈现再到业务逻辑 - 以创建站点访问者看到的页面。从处理开销的角度来看,这比标准的文件读取文件系统服务器要耗时多了。对于大多数Web应用程序来说,这种开销并不是什么大问题。因为大多数Web应用程序只是中小型网站,没有拥有一流的流量。但对于中到高流量的站点,尽可能减少开销是至关重要的,这就是缓存的用武之地。缓存某些内容是为了保存昂贵计算的结果,这样就不必在下次执行计算。
Django框架带有一个强大的缓存系统,可以保存动态页面,因此不必为每个请求计算它们。Django提供不同级别的缓存粒度:可以缓存特定视图的输出,也可以只缓存页面中难以生成的部分或者可以缓存整个站点。
Redis,是一个内存数据库(现在已经支持内存数据持久化到硬盘当中,重新启动时,会自动从硬盘进行加载),由于其性能极高,因此经常作为中间件、缓存使用。
本文档介绍就是Django框架使用Redis数据库来应用缓存框架
之前已经有学习过在Linux环境下使用Redis,这里就试试看使用Windows
redis默认不支持windows,由于一般开发环境在windows,因为需要使用第三方团队维护的windows版本,下载地址:
推荐使用稳定版本

直接减压压缩包,安装好之后,启动redis
启动redis(如果没有配置环境变量,到解压好的文件夹中启用):
redis-server

连接redis数据库(另开一个终端!!!!)
redis-cli
核心配置,在redis.windows.conf下
绑定IP:如果需要远程访问,可以将此注释,或绑定一个真是IP
bind 127.0.0.1
端口:默认为6379
port 6379
日志文件
logfile "Logs/redis_log.txt"
数据库个数
databases 16
基本命令
检测 redis 服务是否启动
PING
设置键值对:
set uname baizhan
取出键值对:
get uname
删除键值对:
del uname
查看所有键值对:
keys *
删除所有的键值对
flushall
运行结果:

django中应用redis,目前一般使用第三方库 django-redis
安装:pip install django-redis
CACHES = {
# default 是缓存名,可以配置多个缓存
"default": {
# 应用 django-redis 库的 RedisCache 缓存类
"BACKEND": "django_redis.cache.RedisCache",
# 配置正确的 ip和port
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
# redis客户端类
"CLIENT_CLASS": "django_redis.client.DefaultClient",
# redis连接池的关键字参数
"CONNECTION_POOL_KWARGS": {
"max_connections": 100
}
# 如果 redis 设置了密码,那么这里需要设置对应的密码,如果redis没有设置密码,那么这里也不设置
# "PASSWORD": "123456",
}
}
}
更多配置项:
LOCATION:设置连接url,譬如:“redis://127.0.0.1:6379/0”,如果要设置redis主从连接,设置列表:[“redis://127.0.0.1:6379/1”, “redis://127.0.0.1:6378/1”],第一个连接是 master 服务器
TIMEOUT:缓存的超时时间,单位秒,默认是 300秒,如果为None,表示缓存永不超时,如果为0,表示缓存立刻超时,相当于不使用缓存
LOCATION:支持使用 本地url符号作为连接,
支持三种 URL scheme :
但是密码放在url中,不是很安全,所以建议使用示例中的方式
OPTIONS:
SOCKET_CONNECT_TIMEOUT:建立连接超时时间,单位秒
SOCKET_TIMEOUT:连接建立后,读写超时时间,单位秒
COMPRESSOR:默认不使用压缩,指定压缩的类,譬如"django_redis.compressors.zlib.ZlibCompressor"
IGNORE_EXCEPTIONS:默认为False,当Redis仅用于缓存时,连接异常或关闭后,忽略异常,不触发异常,可以设置为True,也可以全局设置 DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS=True
PICKLE_VERSION:序列化使用的是pickle,默认情况下使用最新的pickle版本,这里可以设置指定版本号(设置为 -1 也是指最新版本)
CONNECTION_POOL_CLASS:设置自定义的连接池类
PARSER_CLASS:redis.connection.HiredisParser,可以这样设置,使用C写的redis客户端,性能更好
CLIENT_CLASS:设置一些特殊客户端类,譬如:
分片客户端:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": [
"redis://127.0.0.1:6379/1",
"redis://127.0.0.1:6379/2",
],
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.ShardClient",
}
}
}
集群客户端:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": [
"redis://127.0.0.1:6379/1",
],
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.HerdClient",
}
}
}
SERIALIZER:设置序列化,如 “django_redis.serializers.json.JSONSerializer”
全局配置,即设置在settings最外层的配置项:
# 给所有缓存配置相同的忽略行为
DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True
# 设置指定的 logger 输出日志, 需要设置logger
DJANGO_REDIS_LOGGER = 'some.specified.logger'
通过配置获取django_redis的get_redis_connection,进行操作,如下:
from django_redis import get_redis_connection
conn = get_redis_connection("default") # redis.client.StrictRedis
# 支持所有redis的接口
conn.hset('hash_test','k1','v1')
# 也可以手动将数据清除
get_redis_connection("default").flushall()
# 得知连接池的连接数
get_redis_connection("default").connection_pool
运行结果


主要使用两个中间件实现:
FetchFromCacheMiddleware :从缓存中读取数据
UpdateCacheMiddleware :将数据更新到缓存中
该中间件会自动在每个响应中设置几个headers:
如果视图设置了自己的缓存时间(即设置了Cache-Control 的max age),那么页面将被缓存直到到期时间,而不是CACHE_MIDDLEWARE_SECONDS。
使用装饰器 django.views.decorators.cache可以设置视图的到期时间(使用cache_control()装饰器,代码:@cache_control(max_age=3600))或禁用视图的缓存(使用never_cache()装饰器,代码:@never_cache)
如果USE_I18N设置为True,则生成的缓存key将包含当前语言的名称,这样可以轻松缓存多语言网站,而无需自己创建缓存密钥。
如果 USE_L10N设置为True 并且 USE_TZ被设置为True,缓存key也会包括当前语言
在settings的中间件中设置:
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
# 其他中间件...
'django.middleware.cache.FetchFromCacheMiddleware',
]
PS:UpdateCacheMiddleware必须是第一个中间件,FetchFromCacheMiddleware必须是最后一个中间件
然后,将以下必需设置添加到Django的settings文件中:
应用测试
在settings中
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'redis_study.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'redis_study.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# 配置redis缓存
CACHES = {
# default 是缓存名,可以配置多个缓存
"default": {
# 应用 django-redis 库的 RedisCache 缓存类
"BACKEND": "django_redis.cache.RedisCache",
# 配置正确的 ip和port
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
# redis客户端类
"CLIENT_CLASS": "django_redis.client.DefaultClient",
# redis连接池的关键字参数
"CONNECTION_POOL_KWARGS": {
"max_connections": 100
}
# 如果 redis 设置了密码,那么这里需要设置对应的密码,如果redis没有设置密码,那么这里也不设置
# "PASSWORD": "123456",
}
}
}
# 全栈缓存配置
CACHE_MIDDLEWARE_ALIAS = 'default' # 用户存储的缓存别名
CACHE_MIDDLEWARE_SECONDS = 10 # 缓存的秒数
CACHE_MIDDLEWARE_KEY_PREFIX = '' # 生成缓存的前缀
在views中
import time
# @never_cache # 禁用缓存
def global_cache_test(request):
t = time.time()
return HttpResponse(f'全栈缓存测试,时间戳t:{t}')
PS: 若不添加中间件的时候,刷新页面,在规定时间内,时间是不会发生变化的,会被保存到redis中,二次刷新时,从redis中读取。过期后无法进行读取

@cache_page(10, cache='default', key_prefix='redis_app_view_cache_test')
def view_cache_test(request, num=10):
t = time.time()
return HttpResponse(f'视图缓存测试,时间戳t:{t}, num:{num}')
说明:
cache_page除了默认的timeout参数外,还有两个可选的关键字参数
如果多个url指向同一个视图函数,会为每个url建立一个单独的缓存,例如:
通过urls中配置cache_page
在URLconf中指定视图缓存,而不是在视图函数上硬编码装饰器,可以进一步解耦缓存和视图函数之间的关系,使用起来更灵活
from django.contrib import admin
from django.urls import path, include
from django.views.decorators.cache import cache_page
from . import views
urlpatterns = [
path('connect_redis_test/', views.connect_redis_test),
path('global_cache_test/', views.global_cache_test),
path('view_cache_test/<int:num>/', views.view_cache_test),
path('view_cache/', cache_page(10)(views.view_cache_test))
]

使用cache模板标记缓存模板片段
{% load cache %}
{% cache 5000 cache_key %}
缓存内容
{% endcache %}
说明:
cache最少两个参数:
5000: 缓存超时时间,单位秒,如果为None,那么就是永久缓存
cache_key:缓存的key,不能使用变量,只是一个字符串(不要引号),相当于CACHE_MIDDLEWARE_KEY_PREFIX
应用:
<!DOCTYPE html>
{% load cache %}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
{% cache 20 template_cache_key %}
<ul>
<li>使用模板文件缓存</li>
<li>时间戳:{{time}}</li>
</ul>
{% endcache %}
</body>
</html>

可以通过将一个或多个附加参数(可以是带或不带过滤器的变量,变量个数可以是多个,如:{% cache 500 sidebar var1 var2 var3 … %})传递给 cache 来唯一标识缓存片段来执行此操作,示例如下:
{% load cache %}
{% cache 500 sidebar request.user.username %}
指定登录用户的侧边栏
{% endcache %}
如果USE_I18N设置为True,每站点中间件缓存将根据语言进行区分,对于cache模板标记,可以使用模板中可用的特定于 转换的变量 来实现相同的结果,示例如下:
{% load i18n %}
{% load cache %}
{% get_current_language as LANGUAGE_CODE %}
{% cache 600 welcome LANGUAGE_CODE %}
{% trans "Welcome to example.com" %}
{% endcache %}
缓存超时可以是模板变量,使用变量可以避免多次使用同一个值,示例(假设my_timeout设置为 600):
{% cache my_timeout sidebar %} ... {% endcache %}
默认情况下,cache将尝试使用名为“template_fragments”的缓存。如果不存在此缓存,则使用默认的default缓存。可以通过using关键字参数指定使用的缓存,该关键字参数必须是标记的最后一个参数,示例:
{% cache 300 cache_key ... using="localcache" %}
PS:指定不存在的 缓存名 会报错
使用超参数在路由中:
会当成多个路径,分别保存。但是时间保存的值不发生变化

<!DOCTYPE html>
{% load cache %}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
{% cache 20 template_cache_key n %}
<ul>
<li>使用模板文件缓存</li>
<li>时间戳:{{time}}</li>
<li>n:{{n}}</li>
</ul>
{% endcache %}
</body>
</html>
def template_cache(request,n):
context = {
'time':time.time(),
'n':n,
}
return render(request, 'redis_app/template_cache.html',context)
有时不想缓存整个页面数据,而只是想缓存某些费时查询并且基本不会改变的数据,可以通过一个简单的低级缓存API实现,该API可以缓存任何可以安全pickle的Python对象:字符串,字典,模型对象列表等
django.core.cache.caches
from django.core.cache import caches
cache1 = caches['myalias']
cache2 = caches['myalias']
# 判断为True
if cache1 is cache2:
...
说明:
可以通过CACHES类似字典一样的方式访问settings中配置的缓存,在同一个线程中重复请求相同的别名将返回相同的对象
如果指定的 myalias 不存在,将引发 InvalidCacheBackendError
为了线程安全性,为会每个线程返回缓存的不同实例
作为快捷方式, 默认缓存(default)可以使用 django.core.cache.cache :
# 使用 default 缓存
from django.core.cache import cache
# 上面的cache等同于下面的写法
from django.core.cache import caches
cache = caches['default']
django.core.cache.cache
from django.core.cache import cache
# 使用 redis 的一般用法
cache.set('manul_set', 'ok')
manul_set = cache.get('manul_set')
# 可以手动设置 timeout,如果不指定timeout,默认是 300秒
cache.set("key", "value", timeout=None)
# 可以获取key的超时设置(ttl:time to live)
# 返回值的3种情况:
# 0: key 不存在 (或已过期)
# None: key 存在但没有设置过期
# ttl: 任何有超时设置的 key 的超时值
cache.set("foo", "value", timeout=25)
cache.ttl("foo") # 得到 25
cache.ttl("not-existent") # 得到 0
# 让一个值永久存在
cache.persist("foo")
cache.ttl("foo") # 得到 None
# 指定一个新的过期时间
cache.set("foo", "bar", timeout=22)
cache.ttl("foo") # 得到 22
cache.expire("foo", timeout=5)
cache.ttl("foo") # 得到 5
# 支持 redis 分布式锁, 使用 上下文管理器 分配锁
with cache.lock("somekey"):
do_some_thing()
# 使用全局通配符的方式来检索或者删除键
cache.keys("foo_*") # 返回所有匹配的值, 如 ["foo_1", "foo_2"]
# 使用 iter_keys 取代keys 得到 一个迭代器
cache.iter_keys("foo_*") # 得到一个迭代器
next(cache.iter_keys("foo_*")) # 得到 foo_1
# 删除 键
cache.delete_pattern("foo_*") # 支持通配符

from django.shortcuts import render, HttpResponse
from django_redis import get_redis_connection
from django.views.decorators.cache import never_cache, cache_page
# 低级缓存测试
from common.lower_level_cache import get_lower_level_func
def get_result():
time.sleep(5)
return 'lower_level_cache'
def lower_level_cache(request):
# result = get_result()
result = get_lower_level_func('str_key', get_result)
return HttpResponse(f'低级缓存result:{result}')
缓存函数
在公共包中创建py文件,定义缓存函数
from django.core.cache import cache
# 用于缓存数据
def get_lower_level_func(key, func, *args, **kwargs):
'''
key:缓存key
func:调用的函数名, 用于缓存数据的名称
args:可变参数
kwargs:关键字参数
'''
with cache.lock(key+'_lock'):
if key:
#从低级缓存根据key获取值
result = cache.get('key')
if result:
return result
else:
# 调用函数获取结果
result = func(*args, **kwargs)
# 将调用的结果存放到缓存中
cache.set(key,result)
return result

# 配置session的引擎为cache
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
# 此处别名依赖缓存的设置
SESSION_CACHE_ALIAS = 'default'
from django.contrib import admin
from django.urls import path, include
from django.views.decorators.cache import cache_page
from . import views
app_name = 'redis_app'
urlpatterns = [
path('login/', views.login, name='login'),
path('index/', views.index, name='index'),
]
from django.shortcuts import render, HttpResponse, redirect
from django_redis import get_redis_connection
from django.views.decorators.cache import never_cache, cache_page
# session缓存
def login(request):
if request.method == 'GET':
return render(request, 'redis_app/login.html')
elif request.method == 'POST':
uname = request.POST.get('uname')
password = request.POST.get('password')
if uname == 'root' and password == 'root':# 固定账号密码,测试用
# 登录成功
# 将用户名存放到session
request.session['uname'] = uname
return redirect('redis_app:index')
else:
return redirect('redis_app:login')
def index(request):
uname = request.session.get('uname')
if uname: # 跳转到主页
return render(request, 'redis_app/index.html',{'uname':uname})
else:
return redirect('redis_app:login')
页面
login.html页面
<form action="{%url 'redis_app:login'%}" method="POST">
{% csrf_token %}
<p>用户名:<input type = "text" name ="uname" ></p>
<p>密码:<input type = "password" name ="password" ></p>
<p><input type="submit" value="登录"></p>
</form>
index.html页面
<p>欢迎{{uname}}登录</p>
运行结果

我试过重新启动apache,缓存的页面仍然出现,所以一定有一个文件夹在某个地方。我没有“公共(public)/缓存”,那么我还应该查看哪些其他地方?是否有一个URL标志也可以触发此效果? 最佳答案 您需要触摸一个文件才能清除phusion,例如:touch/webapps/mycook/tmp/restart.txt参见docs 关于ruby-如何在Ubuntu中清除RubyPhusionPassenger的缓存?,我们在StackOverflow上找到一个类似的问题:
尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot
当我尝试进行bundle安装时,我的gem_path和gem_home指向/usr/local/rvm/gems/我没有写入权限,并且由于权限无效而失败。因此,我已将两个路径都更改为我具有写入权限的本地目录。这样做时,我进行了bundle安装,我得到:bruno@test6:~$bundleinstallFetchinggemmetadatafromhttps://rubygems.org/.........Fetchinggemmetadatafromhttps://rubygems.org/..Bundler::GemspecError:Couldnotreadgemat/afs/
我一直在Heroku上尝试不同的缓存策略,并添加了他们的memcached附加组件,目的是为我的应用程序添加Action缓存。但是,当我在我当前的应用程序上查看Rails.cache.stats时(安装了memcached并使用dalligem),在执行应该缓存的操作后,我得到current和total_items为0。在Controller的顶部,我想缓存我有的Action:caches_action:show此外,我修改了我的环境配置(对于在Heroku上运行的配置)config.cache_store=:dalli_store我是否可以查看其他一些统计数据,看看它是否有效或我做错
我有一个具有页面缓存的ControllerAction,我制作了一个清扫程序,它使用Controller和指定的Action调用expire_page...Controller操作呈现一个js.erb模板,所以我试图确保expire_page删除public/javascripts中的.js文件,但它没有这样做。classJavascriptsController"javascripts",:action=>"lol",:format=>'js')endend...所以,我访问javascripts/lol.js并呈现我的模板。我验证了public/javascripts/lol.js
我的Controller有这个:caches_action:render_ticker_for_channel,:expires_in=>30.seconds在我的路由文件中我有这个:match'/render_c_t/:channel_id'=>'render#render_ticker_for_channel',:as=>:render_channel_ticker在日志文件中我看到了这个:Writefragmentviews/mcr3.dev/render_c_t/63(11.6ms)我如何手动使它过期?我需要从与渲染Controller不同的Controller使它过期,但即使
我在开发和生产中都使用docker,真正困扰我的一件事是docker缓存的简单性。我的ruby应用程序需要bundleinstall来安装依赖项,因此我从以下Dockerfile开始:添加GemfileGemfile添加Gemfile.lockGemfile.lock运行bundleinstall--path/root/bundle所有依赖项都被缓存,并且在我添加新gem之前效果很好。即使我添加的gem只有0.5MB,从头开始安装所有应用程序gem仍然需要10-15分钟。由于依赖项文件夹的大小(大约300MB),然后再花10分钟来部署它。我在node_modules和npm上遇到了
我正在用RubyonRails重写Django应用程序,并希望为用户保留旧密码。Django使用PBKDF2SHA1作为加密机制。所以我有一个加密密码是这个pbkdf2_sha256$10000$YsnGfP4rZ1IZ$Tpf4922MoNEjuJQA9EG2Elptyt3dMAyzBPUgmunFOW4=原密码是2bulls在Ruby中,我使用PBKDF256gem和base64进行检查。Base64.encode64PBKDF256.dk("2bulls","YsnGfP4rZ1IZ",10000,32)我很期待Tpf4922MoNEjuJQA9EG2Elptyt3dMAyzBP
我正在使用Capybara2.1和Ruby1.9.3,使用selenium驱动程序(带有Minitest和测试单元)来测试网络应用程序。我正在努力解决StaleElementReferenceException问题。我已经看到很多关于该主题的讨论,但我无法找到解决我所面临问题的方法。所以基本上,我试图使用以下代码在我的页面上找到所有分页元素:pagination_elements=page.all('.paginationa')然后我对这些元素做一些断言,例如:pagination_elements.first.must_have_content('1')在这些断言之后,我通过单击下一
HTTP缓存是指浏览器或者代理服务器将已经请求过的资源保存到本地,以便下次请求时能够直接从缓存中获取资源,从而减少网络请求次数,提高网页的加载速度和用户体验。缓存分为强缓存和协商缓存两种模式。一.强缓存强缓存是指浏览器直接从本地缓存中获取资源,而不需要向web服务器发出网络请求。这是因为浏览器在第一次请求资源时,服务器会在响应头中添加相关缓存的响应头,以表明该资源的缓存策略。常见的强缓存响应头如下所述:Cache-ControlCache-Control响应头是用于控制强制缓存和协商缓存的缓存策略。该响应头中的指令如下:max-age:指定该资源在本地缓存的最长有效时间,以秒为单位。例如:Ca