第7章:缓存策略
7.1 代理缓存
Nginx代理缓存允许将后端服务器的响应缓存到本地,减少后端服务器的负载,提高响应速度。
7.1.1 缓存区域配置
配置示例:
http {
# 定义缓存区域
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g
inactive=60m use_temp_path=off;
# ... 其他配置 ...
server {
# ... 其他配置 ...
location / {
# 启用代理缓存
proxy_cache my_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
# 转发到后端服务器
proxy_pass http://backend;
}
}
}指令说明:
proxy_cache_path:定义缓存区域/var/cache/nginx:缓存文件存储路径levels=1:2:缓存目录结构(1级目录,2级子目录)keys_zone=my_cache:10m:缓存区域名称和内存大小max_size=10g:缓存最大大小inactive=60m:缓存项60分钟未被访问则自动删除use_temp_path=off:不使用临时目录,直接写入最终目录
proxy_cache my_cache:启用代理缓存,使用名为my_cache的缓存区域proxy_cache_valid 200 302 10m:状态码200和302的响应缓存10分钟proxy_cache_valid 404 1m:状态码404的响应缓存1分钟proxy_cache_use_stale:在后端服务器出现错误时,使用过期的缓存响应
7.1.2 缓存键设计
缓存键用于唯一标识缓存项,默认使用$scheme$proxy_host$request_uri作为缓存键。
配置示例:
http {
# ... 其他配置 ...
server {
# ... 其他配置 ...
location / {
# 自定义缓存键
proxy_cache_key "$scheme$proxy_host$request_uri$arg_user";
# 基于请求方法的缓存策略
proxy_cache_methods GET HEAD;
# 启用代理缓存
proxy_cache my_cache;
proxy_pass http://backend;
}
}
}指令说明:
proxy_cache_key:自定义缓存键,包含用户参数proxy_cache_methods:只缓存GET和HEAD请求
常见缓存键设计:
- 基本缓存键:
$scheme$proxy_host$request_uri - 包含用户参数:
$scheme$proxy_host$request_uri$arg_user - 包含Cookie:
$scheme$proxy_host$request_uri$http_cookie - 包含请求头:
$scheme$proxy_host$request_uri$http_accept_language
7.1.3 缓存清除策略
7.1.3.1 基于时间的清除
使用proxy_cache_valid指令设置缓存项的有效期,过期后自动清除。
7.1.3.2 手动清除
使用第三方模块ngx_cache_purge手动清除缓存项。
安装ngx_cache_purge模块:
# 下载Nginx源码
wget http://nginx.org/download/nginx-1.18.0.tar.gz
tar -zxvf nginx-1.18.0.tar.gz
# 下载ngx_cache_purge模块
wget https://github.com/FRiCKLE/ngx_cache_purge/archive/refs/tags/2.3.tar.gz
tar -zxvf 2.3.tar.gz
# 重新编译Nginx,添加ngx_cache_purge模块
cd nginx-1.18.0
./configure --with-http_ssl_module --add-module=../ngx_cache_purge-2.3
make
make install配置示例:
http {
# 定义缓存区域
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;
# ... 其他配置 ...
server {
# ... 其他配置 ...
location / {
proxy_cache my_cache;
proxy_pass http://backend;
}
# 缓存清除配置
location ~ /purge(/.*) {
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
proxy_cache_purge my_cache "$scheme$proxy_host$1";
}
}
}使用示例:
# 清除指定URL的缓存
curl http://example.com/purge/path/to/resource7.1.3.3 自动清除(商业版)
Nginx Plus提供了proxy_cache_purge指令的扩展版本,支持更灵活的缓存清除策略,如按前缀清除、按标签清除等。
7.2 浏览器缓存优化
浏览器缓存允许客户端将资源缓存到本地,减少重复请求,提高页面加载速度。
7.2.1 缓存控制头设置
配置示例:
server {
# ... 其他配置 ...
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|eot)$ {
# 设置过期时间为30天
expires 30d;
# 设置Cache-Control头
add_header Cache-Control "public, no-transform";
# 不记录静态资源日志
access_log off;
}
location ~* \.(html|htm|txt)$ {
# 设置过期时间为1小时
expires 1h;
# 设置Cache-Control头
add_header Cache-Control "public, must-revalidate";
}
location ~* \.(xml|json)$ {
# 设置过期时间为10分钟
expires 10m;
# 设置Cache-Control头
add_header Cache-Control "public, must-revalidate";
}
}指令说明:
expires 30d;:设置缓存过期时间为30天add_header Cache-Control "public, no-transform";:允许公共缓存,禁止转换内容add_header Cache-Control "public, must-revalidate";:允许公共缓存,但每次请求都必须验证
Cache-Control指令值:
public:允许公共缓存(如CDN)private:只允许私有缓存(如浏览器)no-cache:需要验证缓存有效性no-store:禁止缓存must-revalidate:过期后必须验证max-age=3600:缓存有效期为3600秒
7.2.2 ETag与Last-Modified
ETag和Last-Modified是用于验证缓存有效性的HTTP头。
配置示例:
server {
# ... 其他配置 ...
location / {
# 启用ETag
etag on;
# 启用Last-Modified
if_modified_since before;
# ... 其他配置 ...
}
}工作原理:
- 服务器响应时发送
ETag和Last-Modified头 - 客户端下次请求时发送
If-None-Match(包含ETag值)和If-Modified-Since(包含Last-Modified值)头 - 服务器验证缓存是否有效:
- 如果缓存有效,返回304 Not Modified,不包含响应体
- 如果缓存无效,返回200 OK,包含完整响应体
验证配置:
# 第一次请求,获取完整响应
curl -I http://example.com/image.jpg
# 第二次请求,应该返回304 Not Modified
curl -I -H "If-None-Match: [ETag值]" -H "If-Modified-Since: [Last-Modified值]" http://example.com/image.jpg7.3 实战项目3:为电商网站配置多级缓存
在这个实战项目中,我们将为电商网站配置多级缓存,包括浏览器缓存、Nginx代理缓存和Redis缓存。
7.3.1 项目准备
1. 安装依赖:
# 安装Redis
sudo apt-get update
sudo apt-get install -y redis-server
# 安装Nginx Redis模块依赖
sudo apt-get install -y libhiredis-dev2. 重新编译Nginx,添加Redis模块:
# 下载ngx_http_redis模块
git clone https://github.com/openresty/redis2-nginx-module.git
# 重新编译Nginx
cd nginx-1.18.0
./configure --with-http_ssl_module --with-http_v2_module --add-module=../redis2-nginx-module
make
make install7.3.2 配置多级缓存
1. 配置Redis缓存:
# 启动Redis
sudo systemctl start redis-server
# 测试Redis连接
redis-cli ping2. 配置Nginx多级缓存:
http {
# 定义代理缓存区域
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=nginx_cache:10m max_size=10g inactive=60m use_temp_path=off;
# Redis服务器配置
upstream redis {
server 127.0.0.1:6379;
keepalive 1024;
}
# 后端应用服务器配置
upstream backend {
server 127.0.0.1:8000;
server 127.0.0.1:8001;
}
server {
listen 80;
server_name example.com;
# 静态资源配置(浏览器缓存)
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|eot)$ {
root /var/www/html;
expires 30d;
add_header Cache-Control "public, no-transform";
access_log off;
}
# 商品详情页配置(多级缓存)
location /product/ {
# 1. 尝试从Redis缓存获取
set $redis_key "product:$uri";
redis2_query get $redis_key;
redis2_pass redis;
# 2. 如果Redis缓存不存在,从Nginx代理缓存获取
error_page 404 = @proxy_cache;
}
# Nginx代理缓存配置
location @proxy_cache {
proxy_cache nginx_cache;
proxy_cache_valid 200 60m;
proxy_cache_key "$scheme$proxy_host$request_uri";
proxy_pass http://backend;
# 3. 将响应缓存到Redis
set $redis_key "product:$uri";
set $redis_ttl 3600;
header_filter_by_lua_block {
local redis_key = ngx.var.redis_key;
local redis_ttl = ngx.var.redis_ttl;
local response_body = ngx.arg[1];
-- 连接Redis
local redis = require "resty.redis";
local red = redis:new();
red:set_timeout(1000);
local ok, err = red:connect("127.0.0.1", 6379);
if ok then
-- 将响应体缓存到Redis
red:setex(redis_key, redis_ttl, response_body);
red:set_keepalive(10000, 100);
end
}
}
# 其他路径配置
location / {
proxy_pass http://backend;
}
}
}7.3.3 验证多级缓存
1. 测试缓存命中率:
# 在Nginx配置中添加缓存命中率日志
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'cache_status=$upstream_cache_status';
access_log /var/log/nginx/access.log main;2. 查看缓存命中率:
grep -o 'cache_status=[^ ]*' /var/log/nginx/access.log | sort | uniq -c预期输出:
100 cache_status=HIT
20 cache_status=MISS
5 cache_status=EXPIRED3. 测试Redis缓存:
# 使用Redis客户端查看缓存
redis-cli keys "product:*"
# 查看缓存内容
redis-cli get "product:/product/123"4. 测试浏览器缓存:
# 第一次请求
curl -I http://example.com/product/123
# 第二次请求(应该返回304 Not Modified)
curl -I -H "If-None-Match: [ETag值]" -H "If-Modified-Since: [Last-Modified值]" http://example.com/product/1237.3.4 缓存清除机制
1. 添加缓存清除接口:
# 缓存清除配置
location ~ /purge(/.*) {
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
# 清除Nginx代理缓存
proxy_cache_purge nginx_cache "$scheme$proxy_host$1";
# 清除Redis缓存
content_by_lua_block {
local redis_key = "product:$1";
local redis = require "resty.redis";
local red = redis:new();
red:set_timeout(1000);
local ok, err = red:connect("127.0.0.1", 6379);
if ok then
red:del(redis_key);
red:set_keepalive(10000, 100);
end
ngx.say("Cache purged for " .. redis_key);
}
}2. 使用示例:
# 清除指定商品的缓存
curl http://example.com/purge/product/1237.3.5 常见问题与解决方案
问题1:缓存不生效
解决方案:
- 检查Nginx配置中的
proxy_cache_path指令是否正确 - 确保缓存目录存在且Nginx有写入权限:
sudo mkdir -p /var/cache/nginx sudo chown -R www-data:www-data /var/cache/nginx - 检查后端服务器是否返回
Cache-Control: no-cache或no-store头
问题2:缓存命中率低
解决方案:
- 增加缓存有效期
- 优化缓存键设计,减少缓存碎片化
- 确保缓存区域足够大,避免频繁淘汰
- 考虑使用CDN缓存静态资源
问题3:缓存更新不及时
解决方案:
- 实现缓存清除机制,在数据更新时主动清除缓存
- 设置合理的缓存有效期,平衡性能和新鲜度
- 使用
stale-while-revalidate和stale-if-error指令,在更新缓存时返回旧缓存
章节总结
在本章中,我们学习了:
代理缓存:
- 缓存区域配置
- 缓存键设计
- 缓存清除策略
浏览器缓存优化:
- 缓存控制头设置
- ETag与Last-Modified
实战项目:
- 为电商网站配置多级缓存(Redis + Nginx代理缓存 + 浏览器缓存)
- 验证缓存命中率
- 实现缓存清除机制
实践练习
- 配置Nginx代理缓存,缓存静态资源和API响应
- 优化浏览器缓存策略,为不同类型的资源设置不同的缓存时间
- 实现基于Redis的多级缓存
- 配置缓存命中率日志,分析缓存效果
- 实现缓存清除机制,在数据更新时主动清除缓存
延伸阅读
下一章:第8章:日志与监控