Apache代理缓存经常被当成简单的开关功能,但真正决定成本控制效果的,是缓存命中率、磁盘I/O与源站回源次数之间的关系。一次缓存命中意味着请求在代理节点直接返回,不产生上游带宽和源站计算资源消耗;一次未命中或缓存过期则会让反向代理重新拉取资源。想用Apache代理缓存控制成本,核心不在于把CacheEnable打开,而在于让高频请求尽量命中、让缓存文件体积合理、让过期策略和实际业务节奏匹配。

一、代理缓存降低成本的基本逻辑
Apache作为反向代理时,请求会经过mod_proxy转发到源站。如果在mod_proxy之外叠加mod_cache和mod_cache_disk,代理节点在转发前先检查缓存目录中是否已经有对应响应。命中时直接返回磁盘文件,节省上行带宽;未命中时回源并将响应写入缓存,供后续请求复用。这个机制看起来简单,但成本差异非常明显。假设某接口平均响应体为50KB,每天请求量为200万次,命中率从60%提升到85%,大约每天减少25万次回源请求,按单次50KB计算,可节省约12.5GB出口流量。实际场景中图片、静态API响应、下载文件等体积更大,节省幅度会更明显。
但缓存不是没有代价。缓存文件写盘会产生I/O,缓存目录过大会拖慢查找速度,错误缓存Set-Cookie或个性化响应可能导致用户数据串号。因此成本控制需要同时关注带宽节省和缓存系统自身的资源消耗。较好的做法是区分公共资源与个性化资源,只缓存可复用的响应,并通过精确的缓存键隔离不同参数。
二、基础缓存配置与磁盘规划
Apache缓存模块通常由mod_cache、mod_cache_disk、mod_proxy和mod_proxy_http组成。启用后,可以通过以下配置搭建一个基本可用的磁盘缓存。
<IfModule mod_cache.c>
CacheRoot /data/apache-cache
CacheEnable disk /
CacheDirLevels 2
CacheDirLength 1
CacheMaxFileSize 2000000
CacheMinFileSize 200
CacheDefaultExpire 3600
CacheMaxExpire 86400
CacheIgnoreHeaders Set-Cookie
CacheIgnoreQueryString On
</IfModule>
CacheRoot指定缓存目录,CacheEnable disk /表示对整个站点启用磁盘缓存。CacheDirLevels和CacheDirLength用于控制子目录层级,避免单个目录堆积大量文件。实际部署时建议把缓存目录放在独立磁盘或分区上,不要与日志、系统盘混用。如果缓存文件数量很大,可使用ext4或XFS,并在挂载时考虑noatime选项,减少读缓存时的元数据写入。
磁盘规划还要考虑缓存淘汰机制。Apache的mod_cache_disk不提供类似Redis的LRU自动淘汰,主要靠文件过期和手工清理。因此需要根据磁盘容量设置CacheMaxFileSize,避免单个大文件占满空间;同时通过CacheMinFileSize放过小文件,减少小响应体的写盘消耗。对于体积很大的下载资源,可以单独设置更高的CacheMaxFileSize,也可以选择不缓存以避免磁盘压力。
如果源站响应带有Set-Cookie,默认缓存这类响应会导致不同用户的会话互相污染。上面的配置中通过CacheIgnoreHeaders Set-Cookie将Set-Cookie从缓存响应的条件中剥离,而不是直接不缓存整个响应。这个细节很关键:很多接口既返回公共数据又附带动态Cookie,完全禁止缓存会浪费命中机会,忽略该头后,代理可以存储数据部分,同时不缓存个性化Cookie。
三、通过缓存键和条件请求提高命中率
缓存键决定了两个请求是否会被判定为同一资源。默认情况下,Apache使用请求URI和部分头信息生成缓存键。如果查询参数顺序不同、大小写不同或带了无关的跟踪参数,就可能造成同一个逻辑资源产生多个缓存副本。可以通过CacheIgnoreQueryString On忽略整个查询字符串,让所有查询参数下的请求共享同一份缓存。但这只适用于查询参数不影响响应内容的接口。如果参数会改变返回数据,则需要保留查询字符串,并通过重写规则在代理前去除无用参数后再缓存。
条件请求是另一个容易被忽略的成本点。源站如果正确返回ETag和Last-Modified,代理在缓存过期后可以使用If-None-Match或If-Modified-Since向源站验证。若内容未变化,源站返回304 Not Modified,代理继续使用旧缓存,避免完整响应体传输。Apache默认会保留这些验证头,但如果源站没有生成,可以在后端应用层补充,或者通过mod_headers在响应中生成ETag。注意不要把不可变的静态资源频繁验证,可以设置较长的CacheMaxExpire,让静态文件在一天甚至更长时间内不再回源。
对需要实时性较高的接口,可以采用stale-while-revalidate思路:缓存过期后先返回旧内容,同时异步回源刷新。Apache的CacheStaleOnError可以在源站出错时继续提供过期内容,避免故障期间流量直接打到已异常的后端。配合CacheLock可以限制并发回源数量,防止缓存过期瞬间的大量请求同时穿透到源站。这些参数对成本控制的意义不只是带宽,还在于保护源站在流量峰值时不被重复请求击穿。
CacheIgnoreQueryString On CacheLock On CacheLockMaxAge 5 CacheStaleOnError On
四、压缩传输与命中率监控
带宽成本除了减少请求次数,还可以通过降低单次传输体积来实现。mod_deflate可以对文本类资源进行gzip压缩,一般HTML、CSS、JavaScript可压缩到原始体积的20%至30%。如果缓存的是压缩后的响应,下游带宽会显著减少。但压缩会消耗代理节点的CPU,并且不同客户端支持的压缩算法不同。通常做法是对text/html、text/css、application/javascript等类型启用gzip,对图片、视频和已压缩的zip不重复压缩。对于支持Brotli的现代浏览器,如果Apache版本和模块允许,也可以提供br压缩,但需要额外测试缓存命中情况。
配置好缓存后,必须监控命中率,否则无法判断成本策略是否生效。可以在虚拟主机中加入如下配置,把缓存状态暴露到响应头,便于日志系统收集。
<IfModule mod_headers.c>
Header set X-Cache-Status "%{cache-status}e"
</IfModule>
这里%{cache-status}e是mod_cache提供的环境变量表达式,命中时通常输出hit,未命中或过期回源时输出miss。通过分析日志中hit与miss的比例,可以判断策略调整是否有效。还可以结合CacheDetailHeader On在响应中输出更多缓存细节,例如过期时间、缓存文件大小等,但生产环境注意不要暴露过多内部信息。
最后算一笔成本账:如果当前未命中请求每次回源消耗100KB,每天有50万次miss,那么每天出口流量约为50GB。若通过调整过期时间和查询参数策略将命中率提升20个百分点,miss次数降到40万次,每天可减少约10GB回源流量。再配合gzip压缩、条件请求和CacheLock限流,源站带宽通常会下降30%以上。这些节省并不需要更换硬件或引入额外CDN,Apache本身就能完成。
Apache代理缓存mod_cache带宽成本修改时间:2026-10-03 21:41:48