Apache HTTP Server除了当静态服务器用,其实也是一台相当能打的反向代理网关。很多人只知道用Nginx做缓存和代理,却忽略了Apache的mod_cache模块同样能完成代理缓存、条件请求协商、请求合并等一系列网关层优化。这篇文章以Windows环境为例,把代理缓存和批处理请求合并两件事讲透,包括httpd.conf的具体配置、缓存命中验证方式,以及在Apache自身不直接支持请求合并的情况下,如何通过外部手段把高频小请求聚合成批量请求。

一、用mod_cache和mod_proxy搭建代理缓存层
Apache的代理缓存能力由两个模块协同完成:mod_cache负责缓存决策与存取,mod_cache_disk(磁盘缓存)或mod_cache_socache(共享内存缓存)负责实际存储。在Windows环境下,默认安装的Apache已经编译了这些模块,只需要在C:\Apache24\conf\httpd.conf中把对应模块的加载注释去掉即可。
第一步是启用模块。打开C:\Apache24\conf\httpd.conf,找到并取消以下几行前面的井号注释:
LoadModule cache_module modules/mod_cache.so LoadModule cache_disk_module modules/mod_cache_disk.so LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so
接着配置磁盘缓存的存储目录。注意Windows下路径要写成反斜杠形式,例如把缓存放在D:\apache_cache\目录下,并且要确保Apache的服务账户对这个目录有读写权限,否则启动时会直接报Permission denied。
<IfModule mod_cache_disk.c>
CacheEnable disk /
CacheRoot "D:/apache_cache"
CacheDirLevels 2
CacheDirLength 1
CacheMaxFileSize 1000000
CacheMinFileSize 1
</IfModule>
这里有个细节值得说明:CacheRoot虽然在Windows上也可以用正斜杠书写,但为了与系统中其他路径保持一致、避免混淆,建议统一使用D:\apache_cache这种反斜杠风格。CacheDirLevels和CacheDirLength控制缓存文件的哈希目录深度,层级太深会增加磁盘寻址开销,太浅则单目录文件过多,一般2比1的组合比较稳妥。
第二步是配置反向代理与缓存规则。假设后端服务跑在8080端口,我们让Apache监听80端口并转发请求:
<IfModule mod_proxy.c>
ProxyPass /api/ http://127.0.0.1:8080/api/
ProxyPassReverse /api/ http://127.0.0.1:8080/api/
<IfModule mod_cache.c>
CacheEnable disk /api
CacheDefaultExpire 3600
CacheIgnoreNoLastMod On
CacheStorePrivate On
Header set X-Cache-Status "%{CACHE_STATUS}e"
</IfModule>
</IfModule>
最后那一行Header配置非常实用,它会把缓存命中状态写进响应头。验证时用浏览器开发者工具或者curl查看X-Cache-Status字段,如果返回MISS说明这次请求穿透到了后端,返回HIT说明直接命中了缓存,返回REVALIDATED则表示缓存过期后通过条件请求确认仍然有效。反复刷新几次同一个接口,第一次MISS、后续HIT,就说明缓存层已经正常工作了。
二、控制缓存行为的关键指令与常见坑
缓存配好只是第一步,真正决定命中率的是对缓存细节的把控。首先要理解后端响应头对缓存的影响。如果后端返回了Cache-Control: no-store或者no-cache,默认情况下Apache不会缓存这类响应。但在内部接口场景里,后端开发者经常习惯性地给所有响应都加上no-store,这时候网关侧可以用CacheIgnoreCacheControl On强制覆盖后端的指令。这个指令威力大也危险,意味着你替业务方做了缓存决策,一定要确认接口数据确实允许短暂滞后。
其次要注意带查询参数的URL。Apache默认会把完整的查询字符串纳入缓存键,也就是说id=1和id=2会分别缓存两份,这本身是对的。但有些系统会在URL里塞时间戳或者随机数做防缓存处理,这类请求在网关层永远无法命中,需要先用RewriteRule把无意义参数剥掉,或者用CacheIgnoreQueryString On配合CacheKeyParsedURL做精细控制。
还有几个高频坑值得单独列出来:
- 缓存了不该缓存的内容:登录态、用户个性化数据被缓存后会出现串号。解决办法是在Location或Proxy标签内用CacheEnable显式限定路径,只对明确的只读接口开缓存。
- 缓存目录膨胀:mod_cache_disk不会自动清理过期条目的磁盘占用,需要配合htcacheclean工具定时清理。Windows下可以用任务计划程序每小时执行一次htcacheclean.exe,命令类似C:\Apache24\bin\htcacheclean.exe -pD:\apache_cache -l512M。
- Cookie干扰:默认情况下带Set-Cookie的响应不会被缓存,如果接口必须返回Cookie又要缓存正文,需要CacheStoreNoStore On加上对Header的处理。
把这些细节处理完,一个稳定的代理缓存层基本就成型了。经验上,只读类查询接口经过缓存层后,后端压力通常能下降六到八成,效果非常直观。
三、批处理请求合并的两种实现思路
请求合并和缓存是互补的两件事:缓存解决的是相同请求的重复回源,合并解决的是大量不同的小请求造成的连接开销。比如一个页面要展示50条商品的库存,前端如果逐条调用查询接口,就是50次代理转发、50次后端数据库往返。把这些请求聚合成一次批量调用,开销会骤降。
需要先说明一个事实:Apache的mod_proxy本身不具备请求合并能力,它是一个逐请求转发模型。所以要实现合并,思路有两条。
第一条思路:在Apache前面加一层合并中间件。用一个轻量的服务(可以是Node.js或者Go写的几十行程序)接收前端的批量请求,拆分后在内存中做短窗口聚合,再把聚合后的请求转发给Apache,Apache继续用自己的缓存和代理能力回源。这种方案里合并逻辑与网关解耦,Apache配置完全不用动。示意代码如下:
// 简化的请求聚合中间件,窗口期内收集相同批接口的调用
const pending = new Map();
function queryStock(id) {
if (pending.has(id)) {
return pending.get(id); // 窗口期内复用同一个Promise
}
const p = new Promise(resolve => {
addToBatch(id, resolve);
});
pending.set(id, p);
return p;
}
// 每10毫秒刷新一次批次,把窗口期内收集的id合并成一次请求
setInterval(flushBatch, 10);
第二条思路:后端直接提供批量接口,Apache做协议适配。如果后端本来就有batch类型的接口,可以在Apache层用mod_rewrite把连续的小请求重写到批量端点上,或者干脆由前端改造,把循环调用改成一次性提交id列表。Apache在这一层扮演的角色是缓存批量结果,因为批量接口的查询组合往往有限,配合第一部分讲的缓存配置,命中率反而更高。
两种方案怎么选?如果前后端都可以改,优先走第二条,直接批量接口加缓存是最干净的结构。如果是改造遗留系统、无法动后端,就选第一条,中间件聚合对后端完全透明。无论哪条路,都建议给合并窗口设置一个上限,比如10毫秒或者最多聚合100个请求,避免高并发时单批次过大反而拖慢响应。
四、整体链路的验证与压测
方案落地前后的效果需要数据说话。Windows下推荐用Apache自带的ab.exe压测工具,位于C:\Apache24\bin\目录。先关掉缓存压一轮基线,再开启缓存压一轮,对比数据:
C:\Apache24\bin\ab.exe -n 2000 -c 100 http://127.0.0.1/api/items?page=1
重点看两个指标:Requests per second和后端服务的日志条数。吞吐量提升是表面,后端日志从2000条降到个位数才是缓存真正发挥作用的证据,因为ab发出的2000个请求里绝大多数都被网关直接用缓存响应了。
请求合并的验证类似,在前端或中间件层打印每个时间窗口的批次大小,观察平均批次数和单批请求数。理想状态是单批聚合了大部分请求,批次数量相比原始请求数下降一个数量级。同时要注意监控合并带来的延迟增加,窗口期本身是额外延迟,10毫秒的窗口对用户几乎无感,但如果为了追求聚合率把窗口拉到100毫秒以上,页面加载的体感就会明显变差,这里需要结合业务场景做权衡。
最后提一句监控,建议在Apache的LogFormat里加入%{X-Cache-Status}o字段,把缓存命中情况写进访问日志,长期统计命中率变化。一旦后端接口结构调整导致缓存键失效,命中率曲线会第一时间反映出来,比用户投诉要快得多。代理缓存加请求合并这套组合拳,配置成本不高,但换来的后端减压效果非常实在,值得在接口密集型系统里优先落地。
Apache代理缓存批处理请求合并mod_cache配置修改时间:2026-09-04 04:04:52