代理缓存的工作方式是把频繁请求的网络资源副本存储在代理服务器上,使后续对同一内容的请求可直接由缓存提供,而无需再次从源服务器获取。
代理缓存概述
代理服务器充当客户端与源服务器之间的中介。配置为缓存模式后,它会拦截客户端对网络内容(如 HTML 页面、图片、样式表或脚本)的请求。如果所请求的资源存在于代理的本地存储中且被判定为新鲜,代理会立即返回该副本;否则代理会将请求转发至源服务器,取回资源并返回给客户端,同时在缓存中保存一份副本以备后用。
代理缓存的优势
实施代理缓存可带来多项运维优势:
- 降低延迟: 由地理位置更近的代理缓存提供的内容,比从远端源服务器获取的内容更快到达客户端,从而提升用户感知的应用性能。
- 减少带宽消耗: 通过提供缓存内容,代理最大限度减少了在外部网络链路上重复下载相同数据的需求,尤其对高频访问的资源可显著节省带宽。
- 减轻源服务器负载: 缓存分流了源服务器的大量请求,使其能处理更多独特请求或以更低负荷运行,从而避免过载并提升响应速度。
- 改善用户体验: 更快的加载速度和更稳定的内容分发直接为最终用户带来更好的体验。
代理缓存如何工作:请求流程
缓存过程包含以下步骤:
- 客户端请求: 客户端(例如浏览器)向代理服务器发送获取某资源的 HTTP 请求。
- 缓存查找: 代理服务器收到请求,在本地缓存中查找该资源的已存副本。缓存键通常由 URL 以及可能的其他请求头派生而来。
- 缓存命中(新鲜): 若在缓存中找到有效且新鲜的资源副本,代理立即将该缓存副本返回给客户端。这是最快的路径。
- 缓存命中(过期/需验证): 若找到副本但已判定为过期(新鲜期已到期),代理会向源服务器发起条件请求。该请求包含
If-Modified-Since或If-None-Match等验证头。- 如果源服务器返回
304 Not Modified,说明缓存副本仍然有效,代理将其返回给客户端并更新新鲜度信息。 - 如果源服务器返回资源的新版本(
200 OK),代理用新内容更新缓存,返回给客户端并更新新鲜度信息。
- 如果源服务器返回
- 缓存未命中: 若缓存中没有该资源的副本,或源服务器指出缓存副本已失效并发送了新内容,代理会将客户端的原始请求转发至源服务器。
- 源服务器响应: 源服务器处理请求,并将资源发回代理。
- 缓存与交付: 代理从源服务器接收资源,(若符合缓存条件)在缓存中保存一份副本,然后将资源转发给客户端。
缓存失效与新鲜度
保持缓存新鲜度对确保客户端获得最新内容至关重要。HTTP 缓存机制主要依赖源服务器提供的响应头。
HTTP 缓存头
源服务器使用特定的 HTTP 响应头来指示代理(以及客户端浏览器)如何缓存内容:
-
Cache-Control: 缓存指令中最主要、功能最强的头部。max-age=<seconds>:指定资源被视为新鲜的最长时间。no-cache:强制代理在使用缓存副本前先与源服务器重新验证,即使该缓存条目尚未过期。它并不表示"不要缓存"。no-store:禁止代理在任何缓存中存储请求或响应的任何部分。public:表示该资源可被任何缓存存储,包括共享代理缓存。private:表示该资源面向单个用户,只能由私有的浏览器缓存存储,不能由共享代理缓存存储。must-revalidate:缓存条目过期后强制与源服务器重新验证。proxy-revalidate:与must-revalidate类似,但仅适用于共享代理缓存。
http Cache-Control: public, max-age=3600 Cache-Control: no-cache Cache-Control: no-store -
Expires: HTTP/1.0 时代的较旧头部,指定响应在某个日期/时间之后被视为过期。两者同时存在时,Cache-Control: max-age优先。http Expires: Thu, 01 Dec 1994 16:00:00 GMT -
ETag(Entity Tag): 源服务器为资源的特定版本分配的不透明标识符。资源变化时会生成新的ETag。代理在条件请求中使用ETag。http ETag: "67ab3246a-543-12345678" -
Last-Modified: 表示资源在源服务器上最后一次修改时间的时间戳。代理在条件请求中使用该值。http Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
条件请求
当缓存资源的新鲜期到期时,代理会向源服务器发送条件请求,以检查资源是否发生变化。
-
If-None-Match: 携带缓存响应中的ETag发送。若源服务器上的ETag匹配,服务器返回304 Not Modified。http GET /images/logo.png HTTP/1.1 Host: example.com If-None-Match: "67ab3246a-543-12345678" -
If-Modified-Since: 携带缓存响应中的Last-Modified日期发送。若资源自该日期以来未被修改,服务器返回304 Not Modified。http GET /styles/main.css HTTP/1.1 Host: example.com If-Modified-Since: Tue, 15 Nov 1994 12:45:26 GMT
缓存一致性的挑战
保持完美的缓存一致性(确保所有客户端始终获得资源的最新版本)可能相当复杂。常用策略包括:
* 较短的 TTL: 牺牲部分缓存效率以换取更高的新鲜度。
* 缓存失效 API: 由源服务器显式通知代理清除特定的缓存项。
* 缓存击穿(Cache Busting): 为频繁变化的资源 URL 追加唯一查询参数(例如 ?v=123),迫使代理获取新版本。
代理缓存的类型
代理缓存可按部署位置和用途分类:
正向代理缓存
正向代理缓存位于客户端与互联网之间。客户端需显式配置为使用该代理。这类缓存常见于企业网络,用于减少出站带宽并提升内部用户的访问速度。它代表一组客户端行事。
反向代理缓存
反向代理缓存位于一台或多台源服务器之前。客户端连接到反向代理,由其将请求转发至相应的源服务器。反向代理可缓存源服务器的响应,从而为源服务器分流并提升外部客户端的性能。它常用于负载均衡、SSL 卸载和内容分发。
透明代理缓存
透明代理无需客户端做任何配置即可拦截客户端请求。网络路由被配置为将流量重定向经过代理,客户端并不知道自己正在使用代理。ISP 或网络管理员常用这种方式在无需逐台设备设置的情况下提升整体网络性能。
缓存键的生成
为高效存取资源,代理必须为每个资源生成唯一的"缓存键"。该键决定后续请求是否与某个缓存条目匹配。缓存键的主要组成部分通常包括:
- URL: 协议、主机、端口、路径和查询参数通常构成缓存键的核心。
- HTTP 方法: 一般只缓存
GET和HEAD请求。 - Vary 头: 若源服务器返回
Vary头(例如Vary: Accept-Encoding, User-Agent),代理必须把指定请求头的值纳入缓存键。这样可确保同一资源的不同表示形式(例如 gzip 压缩与未压缩、移动端与桌面端)被分别缓存。
存储机制与淘汰策略
代理缓存采用多种存储机制和策略:
-
存储:
- RAM(内存): 访问最快,用于极热门内容或元数据,容量有限。
- 磁盘(SSD/HDD): 比 RAM 慢,但容量大得多,是存放大批量内容的常见选择。
- 混合: 用 RAM 存放元数据和频繁访问的小对象,用磁盘存放较大或访问频率较低的内容。
-
淘汰策略: 当缓存存储达到上限时,代理必须决定移除哪些条目以腾出空间。常见策略包括:
- LRU(Least Recently Used): 移除最久未被访问的条目。
- LFU(Least Frequently Used): 移除被访问次数最少的条目。
- FIFO(First-In, First-Out): 移除缓存中最早的条目。
配置注意事项
有效的代理缓存需要仔细配置:
- 缓存容量: 在可用存储与待缓存内容量之间取得平衡。容量过小则命中率下降;过大则磁盘 I/O 可能成为瓶颈。
- 默认 Time-to-Live(TTL): 为缺少显式缓存头的资源设置默认新鲜期。这是一种兜底手段,可能影响新鲜度。
- 绕过规则: 定义规则,避免缓存特定 URL、敏感内容或绝不应缓存的动态资源(例如返回个性化数据的 API 端点、已认证会话)。
- HTTPS 缓存: 由于加密,缓存 HTTPS 流量更为复杂。代理往往必须解密流量(需要自己的 SSL 证书和密钥)才能检查响应头并缓存内容,随后再重新加密。这通常在反向代理或客户端信任代理证书的显式正向代理上完成。不解密的透明 HTTPS 缓存只能缓存
CONNECT响应,无法缓存其中的实际内容。 - 日志与监控: 对于观察缓存命中率、发现缓存效率问题和排障必不可少。
常见的代理缓存软件
实施代理缓存有多种成熟方案可选:
- Squid: 广泛使用的开源正向与反向代理,具备丰富的缓存能力。
- Varnish Cache: 专为缓存网络内容设计的高性能 HTTP 加速器(反向代理)。以 VCL(Varnish Configuration Language)著称,可编写高度灵活的缓存策略。
- Nginx: 主要作为 Web 服务器和反向代理,Nginx 提供强大的缓存功能,尤其是在配置为反向代理时。
- Apache HTTP Server: 可通过
mod_cache、mod_disk_cache等模块配置为缓存代理。
