上一篇 下一篇 分享链接 返回 返回顶部

Nginx反向代理缓存如何配置与验证?香港服务器高并发场景实操

发布人:Minchunlin 发布时间:2026-10-07 15:18 阅读量:11

开启了 proxy_cache,页面却仍然每次回源;把缓存时间设为一分钟,内容却过了几分钟还没更新。这两种现象并不矛盾:Nginx是否读取、写入和继续使用缓存,取决于缓存键、请求身份、源站响应头以及过期处理策略,而不只是某一个配置项。

在香港服务器的高并发场景中,正确做法是先划定“哪些响应可以共享”,再配置缓存区域、缓存键、绕过条件和失效方式,最后通过响应头、访问日志及受控压测验证。缓存能够减少重复回源和应用计算,但不能直接消除访问链路时延,也不能替代连接数、文件描述符和带宽容量检查。

围绕香港服务器上的网站、接口与数据库业务,A5数据提供入门建站、Xeon Gold及AMD EPYC等物理服务器方案,搭配SSD或NVMe存储,并提供CN2与国际带宽选择,为Nginx缓存文件、上游应用和日志数据提供相应的计算、存储与网络资源。面向不同访问区域的业务,A5数据还覆盖美国、日本、韩国、新加坡等地区的服务器产品,便于结合部署位置与线路需求构建多地区服务架构。

一、概念边界:反向代理缓存应该放在哪一层

页面、对象、查询与边缘缓存不是同一件事

Nginx反向代理缓存存储的是上游返回的HTTP响应,通常包括响应头和响应体。它适合缓存公开页面、公共JSON接口、下载文件等可复用内容,不会自动理解数据库查询结果或业务对象的失效关系。

缓存层级适合的内容常见失效方式验证重点
Nginx反向代理缓存公开页面、公共接口、可重复下载内容TTL、缓存键版本、定向清理是否命中、是否回源、内容是否一致
应用对象缓存商品对象、配置对象、聚合结果更新后删除、版本号、事件通知更新是否传播、对象是否串用
查询结果缓存重复查询、统计结果查询键失效、短TTL、数据版本参数与权限是否进入键
CDN边缘缓存静态资源、允许边缘共享的页面刷新、版本化URL、TTL不同节点是否更新、回源比例

例如,商品详情页包含商品介绍、登录用户价格和购物车数量。公开介绍可以缓存,但整个页面未必能共享。更合理的方案可能是缓存公共页面骨架,让用户价格和购物车接口单独请求。

一、概念边界:反向代理缓存应该放在哪一层配图

判断能否使用共享缓存,关键不是URL是否相同,而是不同用户在同一个缓存键下,是否应当得到相同响应。

香港服务器只是部署位置,并不改变这一原则。面向不同地区访问者时,命中缓存可以减少香港节点到源站的往返及应用处理时间;访问者到香港节点的网络时延仍然存在。

为什么不建议直接缓存整个网站

整站启用缓存容易把登录态、购物车、管理后台和用户专属接口放入共享区域。即使源站通常返回正确的 Cache-Control,也不应该把数据隔离完全寄托在某一个响应头上。

较稳妥的起点是:

  • 仅对白名单路径启用缓存。
  • 只缓存GET和HEAD请求。
  • 带Cookie或Authorization的请求先绕过缓存。
  • 不忽略源站的 Set-Cookie、Cache-Control 和 Vary。
  • 登录、支付、管理后台及用户专属数据默认不进入共享缓存。

“所有Cookie都绕过”会降低命中率,却便于建立安全基线。后续如需允许统计类Cookie,可以在明确业务语义后细化规则,而不是直接忽略全部Cookie。

二、工作机制:从一个请求理解配置如何生效

缓存键决定哪些请求能够复用

请求进入Nginx后,缓存模块根据缓存键查找对象。没有可用对象时,请求转发给上游;响应符合缓存条件,才会被写入缓存。

二、工作机制:从一个请求理解配置如何生效配图

一个简单的公开内容缓存键可以是:

proxy_cache_key "$scheme|$host|$request_uri|public-v1";

这里分别保留协议、主机名、完整请求URI和缓存命名空间版本。$request_uri 包含原始查询字符串,因此:

/public/catalog.json?page=1
/public/catalog.json?page=2

不会混用同一份响应。

代价是参数顺序不同也可能生成不同对象,例如 ?page=1&sort=price 与 ?sort=price&page=1。先保证正确性,再考虑参数规范化。不能为了提高命中率,直接从缓存键删除会影响内容的参数。

示例没有加入请求方法,是因为只允许GET、HEAD参与缓存,并保留Nginx默认的HEAD转GET缓存行为。若修改该行为,或者让其他方法进入缓存,就必须重新审查方法与缓存键的关系。

TTL、淘汰和过期复用分别解决什么问题

这三个概念容易混淆:

  • TTL:对象在多长时间内可以被视为新鲜。
  • inactive:对象在多长时间未被访问后,可以从缓存中移除。
  • 过期复用:对象过期后,在更新或上游故障期间,是否仍允许返回旧内容。

inactive=30m 不意味着内容可以新鲜使用30分钟。它描述的是未访问对象的清理条件,而不是内容更新时间。

同样,允许 stale 后,TTL也不再是“旧内容最多出现多久”的严格承诺。对于库存、权限、价格承诺等要求及时更新的数据,不应套用公开资讯页的过期复用策略。

准备条件与可执行配置

以下示例用于Linux上的Nginx测试环境:上游应用监听 127.0.0.1:9000,公开测试接口为 /public/catalog.json,Nginx测试入口仅监听本机8080端口。该接口应返回稳定的公共内容,不应按Cookie、Authorization或访问者身份生成差异数据。

先核对实际安装和配置布局:

nginx -v
nginx -V 2>&1
sudo nginx -T
ps -eo user,args | grep '[n]ginx: worker'
df -h /var/cache
df -i /var/cache

nginx -T 用于确认配置文件及其包含关系,也可能显示内部地址等信息,不要直接公开完整输出。

缓存目录必须允许worker用户写入。下面仅适用于已确认worker用户和组均为 www-data、且目标目录尚未创建的环境:

getent passwd www-data
getent group www-data
sudo install -d -o www-data -g www-data -m 0750 /var/cache/nginx/a5-public

此操作只针对新建的缓存目录,不应递归修改现有业务目录权限。若worker身份不同,应替换用户和组;若启用了SELinux或AppArmor,还需核对相应访问规则。回退时先停用引用该目录的配置,确认没有使用者,再按变更记录处理目录,不要直接清空正在使用的缓存。

修改前备份配置:

sudo cp -a /etc/nginx "/root/nginx-backup-$(date +%Y%m%d-%H%M%S)"

下面配置必须放在 http 上下文内。例如,只有确认 conf.d/*.conf 在 http 内被包含,才可放入相应文件;不要直接追加到 server 或 location 内。

proxy_cache_path /var/cache/nginx/a5-public
    levels=1:2
    keys_zone=a5_public:64m
    max_size=5g
    inactive=30m
    use_temp_path=off;

map $request_method $skip_method {
    default 1;
    GET     0;
    HEAD    0;
}

map $http_cookie $skip_cookie {
    default 1;
    ""      0;
}

map $http_authorization $skip_auth {
    default 1;
    ""      0;
}

map "$skip_method$skip_cookie$skip_auth" $skip_cache {
    default 1;
    "000"   0;
}

log_format a5_cache
    '$time_iso8601 host=$host method=$request_method uri=$uri '
    'status=$status cache=$upstream_cache_status '
    'rt=$request_time urt=$upstream_response_time '
    'upstream=$upstream_addr us=$upstream_status';

upstream a5_origin {
    server 127.0.0.1:9000;
    keepalive 32;
}

server {
    listen 127.0.0.1:8080 default_server;
    server_name _;
    return 404;
}

server {
    listen 127.0.0.1:8080;
    server_name cache-demo.example.com;

    access_log /var/log/nginx/a5-cache-access.log a5_cache;

    location ^~ /public/ {
        proxy_pass http://a5_origin;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_connect_timeout 2s;
        proxy_read_timeout 10s;

        proxy_cache a5_public;
        proxy_cache_key "$scheme|$host|$request_uri|public-v1";
        proxy_cache_methods GET HEAD;
        proxy_cache_valid 200 60s;

        proxy_cache_bypass $skip_cache;
        proxy_no_cache $skip_cache;

        proxy_cache_lock on;
        proxy_cache_lock_timeout 5s;
        proxy_cache_lock_age 5s;

        proxy_cache_background_update on;
        proxy_cache_use_stale updating error timeout
            http_500 http_502 http_503 http_504;

        add_header X-Cache-Status $upstream_cache_status always;
    }

    location / {
        proxy_pass http://a5_origin;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

这个示例有几个重要约束:

keys_zone=64m 保存缓存索引等元数据,不代表响应体全部存放在64MB内存中;响应内容主要存放在缓存目录,操作系统可能通过页缓存加速读取。max_size=5g 是缓存管理目标,不是写入瞬间不可超过的硬限制,应额外预留磁盘空间。

proxy_cache_bypass 控制是否读取已有缓存,proxy_no_cache 控制是否保存本次上游响应。带登录态请求需要同时考虑这两个方向,避免出现“没有读取缓存,却把个人响应写进去”的情况。

proxy_cache_valid 200 60s 给出200响应的配置级缓存时间,但源站的 X-Accel-Expires、Cache-Control、Expires 等可能影响实际缓存行为。示例没有覆盖这些头,也没有忽略 Set-Cookie 或 Vary,因此一分钟不是所有响应的固定有效期。

缓存锁用于减少同一个冷缓存对象被同时重建。等待超过锁超时、更新请求过慢等情况下,仍可能出现额外回源,不能把它理解为任何情况下都只会有一个上游请求。

完成修改后:

sudo nginx -t
sudo systemctl reload nginx

第二条仅适用于由systemd管理、服务名为 nginx 的环境。语法检查失败时不要reload;需要回退时,恢复本次修改的文件,重新执行 nginx -t,通过后再reload。生产使用时,应将验证过的缓存逻辑迁入实际HTTPS站点,并核对可信主机名及上游Host要求,不要直接开放测试入口。

三、影响因素:命中率之外,还要检查连接和资源

连接数不是并发请求数

高命中率可以显著减少上游连接,但Nginx仍需维持客户端连接。缓存未命中时,一个活动请求通常还会占用上游连接资源。

下面是容量起点,不是所有香港服务器都应照搬的推荐值:

# 主配置顶层
worker_processes auto;

# 放入现有 events 块,不要重复创建 events
events {
    worker_connections 4096;
}

worker_connections 是每个worker的连接额度,不只计算客户端连接,还包括上游等连接。HTTP/2又允许同一个连接承载多个请求,所以不能直接把“worker数量×4096”当成业务并发能力。

连接额度还受进程文件描述符限制影响:

systemctl show nginx -p LimitNOFILE

该命令仅适用于对应systemd服务。还应检查实际worker进程:

sudo cat /proc//limits

将 替换为真实PID。文件描述符还用于日志、缓存文件等,不能全部留给网络连接。若日志出现 worker_connections are not enough 或 too many open files,才有针对性地检查连接额度、运行中进程限制及流量来源。

上游keepalive不是连接总上限

示例中的 keepalive 32 表示每个worker为该上游组保留的空闲连接数量,并不限制活动连接总数。它减少频繁建连的成本,但不能替代应用端的连接池、数据库容量和并发限制。

缓存命中时,上游连接需求下降;冷缓存和大规模失效时,需求可能突然恢复。因此,压测只覆盖热缓存,容易高估系统的安全容量。

命中缓存仍然消耗带宽与磁盘资源

以一个估算场景说明:每个响应体为20KB,按十进制计算,1KB为1000字节;每秒返回2000次,则响应体流量为:

20,000字节 × 2,000次/秒 = 40,000,000字节/秒
40,000,000 × 8 ÷ 1,000,000 = 320Mbps

这是未计协议开销的响应体流量,也未考虑压缩和客户端中断。即使全部命中缓存,香港服务器对外发送这些内容仍然需要相应带宽。

大量小对象更容易受到缓存元数据、文件数量和随机I/O影响;大对象则更容易受到磁盘吞吐与出口带宽影响。CPU、带宽和磁盘中任何一项饱和,命中率都可能很好,但响应时间仍然上升。

四、验证方法:证明可缓存、正确命中并能按预期更新

先验证MISS到HIT,而不是直接压测

测试接口应存在并返回200,源站不应设置禁止缓存的头,也不应返回 Set-Cookie。以下命令发起GET请求,只丢弃响应体,不使用 curl -I,避免把HEAD行为与GET验证混在一起:

HOST=cache-demo.example.com
URL=http://127.0.0.1:8080/public/catalog.json

curl -sS -H "Host: $HOST" -D - -o /dev/null "$URL"
curl -sS -H "Host: $HOST" -D - -o /dev/null "$URL"

对于此前不存在的新鲜测试对象,预期先出现:

X-Cache-Status: MISS

随后出现:

X-Cache-Status: HIT

如果一直是MISS,应先查看状态码及源站响应头,再检查目录权限和错误日志。常见原因包括 Cache-Control: private/no-store/no-cache、Set-Cookie、Vary: *、源站自定义缓存控制,以及每次请求都改变了查询参数。

四、验证方法:证明可缓存、正确命中并能按预期更新配图

若显示BYPASS,则优先核对方法、Cookie和Authorization;若没有缓存状态,则检查请求是否进入了启用缓存的location。浏览器刷新、CDN和本地浏览器缓存可能干扰观察,本机直连测试有助于隔离这些因素。

必须验证隔离,不能只看到HIT就算成功

curl -sS -H "Host: $HOST" \
  -H "Cookie: session=test-only" \
  -D - -o /dev/null "$URL"

curl -sS -H "Host: $HOST" \
  -H "Authorization: Bearer test-only" \
  -D - -o /dev/null "$URL"

这两次请求应绕过缓存;若上游接受请求,通常可以看到BYPASS。不要在命令、截图或日志中使用真实令牌。

还应比较不同查询参数的响应体,确认它们没有串用缓存。对于多语言、地区定价或自定义请求头影响内容的接口,需要另外检查 Vary 或显式缓存键设计。并非所有业务差异都能由Nginx自动识别。

结合日志解释命中和回源

查看测试日志:

sudo tail -n 50 /var/log/nginx/a5-cache-access.log

以下是说明逻辑的示例结果,不代表本站实测:

status=200 cache=MISS rt=0.083 urt=0.081 upstream=127.0.0.1:9000 us=200
status=200 cache=HIT rt=0.002 urt=- upstream=- us=-

第一行说明该请求访问了上游,第二行说明响应来自缓存。request_time 包含Nginx处理及向客户端发送等时间,不等于纯应用计算耗时;客户端接收较慢时,HIT请求也可能持续较久。

命中率的分母应先说明范围:统计“允许缓存的公共GET请求”,与统计“整个网站请求”,会得到不同结果。应将HIT、MISS、BYPASS、EXPIRED、STALE、UPDATING分别观察,避免把业务绕过或故障复用误当成普通命中。

验证TTL、后台更新与失效

若源站没有覆盖配置级TTL,可以在缓存建立后等待超过60秒,再请求同一URL。由于示例开启后台更新和过期复用,首次请求可能返回STALE,更新期间的其他请求可能返回UPDATING,更新完成后再出现HIT。

状态受到请求时序影响,不必强求每次都出现同一序列。真正需要核对的是:

  1. 源站内容更新后,新内容最终能否进入缓存。
  2. 更新期间允许返回旧内容多久。
  3. 上游失败时,返回旧内容是否符合业务规则。

需要紧急失效时,可将键中的 public-v1 改为 public-v2,通过语法检查后reload。新请求会进入新的缓存命名空间,旧文件等待缓存管理器清理,因此会产生冷缓存流量和额外磁盘占用。

这不是定向刷新接口,也不应为刷新一个页面就频繁修改全站版本。不要默认普通安装已具备可调用的清理接口,应先核对版本、模块和访问控制。

最后进行受控并发验证

在获授权的测试环境中,确认已安装 wrk,可先预热同一个URL,再进行短时测试:

wrk -t4 -c100 -d30s \
  -H "Host: cache-demo.example.com" \
  http://127.0.0.1:8080/public/catalog.json

从较低并发逐步增加,同时观察错误率、响应延迟、Nginx日志、应用请求量、CPU、磁盘和网络。分别测试热缓存、冷缓存及接近过期时的行为,不要只测持续HIT。

四、验证方法:证明可缓存、正确命中并能按预期更新配图

本机压测适合隔离服务端处理能力,但压测工具也消耗同一台机器的资源,且不代表真实访问者到香港的链路表现。外部验收应从目标访问地区测试,并区分DNS、连接、TLS、首字节和完整下载时间。

五、适用限制:把性能收益与数据新鲜度一起验收

对公开文章、公共商品介绍、更新频率不高的JSON响应,短TTL加缓存锁通常是容易验证的起点;允许短时旧内容的业务,可以进一步评估后台更新和故障复用。

对账户余额、订单状态、权限判断、实时库存和用户专属价格,更合适的起点是“不使用共享HTTP缓存”,或者先在应用层设计严格的身份隔离和失效机制。Nginx不知道某次数据库更新应让哪些页面同时失效,单靠TTL无法保证跨页面一致性。

如果香港服务器前面还有CDN,必须分别验证两层。Nginx的 X-Cache-Status 响应头也可能被CDN缓存,边缘返回的HIT字样不一定代表本次请求真的到达Nginx。应结合CDN自身状态、Nginx访问日志和源站日志确认路径。

上线验收可以围绕四个问题完成:公共请求是否稳定命中,身份请求是否正确绕过,更新是否在业务允许的时间内生效,冷缓存和故障期间上游是否仍能承受回源。四项都通过后,再提高缓存覆盖范围和并发额度;若只是HIT变多,却出现错误内容、长时间旧数据或带宽饱和,就不应继续以命中率作为优化目标。