霸王餐API网关层缓存:Nginx Proxy Cache与Cache-Control细节
缓存架构设计目标
"吃喝不愁"App的霸王餐活动接口(如 /api/v1/campaigns/{id})具有高读低写、内容时效性要求适中的特点(数据变更后允许5秒内延迟)。为减轻后端服务压力,我们在 Nginx 网关层部署 proxy_cache,结合后端返回的 Cache-Control 头实现智能缓存策略。
Nginx Proxy Cache 基础配置
nginx
http {
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=campaign_cache:10m max_size=2g inactive=60m use_temp_path=off;
server {
listen 80;
server_name api.eatfree.juwatech.cn;
location /api/v1/campaigns/ {
proxy_cache campaign_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 5s;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;
proxy_pass http://backend-service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
add_header X-Cache-Status $upstream_cache_status;
}
}
}
关键参数说明:
keys_zone=campaign_cache:10m:分配10MB内存存储缓存键(约可存8万条);inactive=60m:60分钟未被访问则自动清理;proxy_cache_valid 200 5s:仅对状态码200缓存5秒;proxy_cache_background_update on:缓存过期时后台更新,避免请求堆积;add_header X-Cache-Status:用于调试(HIT/MISS/BYPASS)。

后端响应头控制(Java示例)
在 juwatech.cn.controller.CampaignController 中精确控制缓存行为:
java
package juwatech.cn.controller;
import org.springframework.http.CacheControl;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class CampaignController {
@GetMapping("/api/v1/campaigns/{id}")
public ResponseEntity<CampaignDetail> getCampaign(@PathVariable String id) {
CampaignDetail detail = fetchFromService(id);
if (detail == null || detail.isExpired()) {
// 不可缓存的响应
return ResponseEntity.noContent().build();
}
// 公开缓存,最大5秒,必须重新验证
CacheControl cc = CacheControl.maxAge(5, TimeUnit.SECONDS)
.mustRevalidate()
.cachePublic();
return ResponseEntity.ok()
.cacheControl(cc)
.header("Vary", "Accept-Encoding")
.body(detail);
}
private CampaignDetail fetchFromService(String id) {
// 模拟DB或RPC调用
return new CampaignDetail(id, "霸王餐第" + id + "期", false);
}
}
生成的响应头:
HTTP/1.1 200 OK
Cache-Control: max-age=5, must-revalidate, public
Vary: Accept-Encoding
Nginx 与 Cache-Control 的交互规则
Nginx 默认不 完全遵循 Cache-Control,需显式启用:
nginx
location /api/v1/campaigns/ {
proxy_cache campaign_cache;
proxy_ignore_headers Cache-Control Expires; # ❌ 错误!会忽略后端控制
}
正确做法是保留 后端头,并配合 proxy_cache_valid 作为兜底:
nginx
# 不设置 proxy_ignore_headers
proxy_cache_valid 200 10s; # 仅当后端未返回 Cache-Control 时生效
若后端返回 Cache-Control: max-age=5,Nginx 将优先使用该值,覆盖 proxy_cache_valid。
绕过缓存的场景处理
运营人员需立即看到变更,可通过带特殊Header绕过缓存:
nginx
location /api/v1/campaigns/ {
set $nocache "";
if ($http_x_admin_bypass = "true") {
set $nocache "true";
}
proxy_cache_bypass $nocache;
proxy_no_cache $nocache;
# ... 其他配置
}
前端调用时添加:
http
GET /api/v1/campaigns/123 HTTP/1.1
X-Admin-Bypass: true
此时 Nginx 直接回源,且不缓存该响应。
缓存键精细化设计
默认 $request_uri 包含查询参数,但某些参数不应影响缓存(如 ?utm_source=xxx):
nginx
map $request_uri $cache_uri {
default $request_uri;
~^(?P<path>/api/v1/campaigns/\d+)(\?.*)?$ $path;
}
server {
location /api/v1/campaigns/ {
proxy_cache_key "$scheme$request_method$host$cache_uri";
# ...
}
}
确保 /api/v1/campaigns/123?debug=true 与 /api/v1/campaigns/123 共享同一缓存。
验证与监控
-
查看缓存状态:
curl -I https://api.eatfree.juwatech.cn/api/v1/campaigns/123X-Cache-Status: HIT -
检查磁盘缓存:
ls /var/cache/nginx/ -
日志记录缓存命中:
nginxlog_format cache '$remote_addr - $upstream_cache_status [$time_local] ' '"$request" $status $body_bytes_sent'; access_log /var/log/nginx/cache.log cache;
注意事项
- 缓存时间不宜过长,霸王餐数据变更频繁;
- 敏感接口(如含用户ID)禁止缓存;
- 使用
Vary头区分不同客户端(如 Accept-Encoding); - 定期清理磁盘缓存目录,防止 inode 耗尽。
本文著作权归吃喝不愁app开发者团队,转载请注明出处!