在 Web 架构里,Apache 常作为反向代理挡在应用服务器前面。当客户端请求集中访问某些读多写少且生成成本高的接口时,如果每次都转发到后端,数据库和计算资源很快成为瓶颈。代理缓存可以把响应暂存起来复用,而令牌桶整形则负责约束穿过代理的请求速率。把两者结合起来,既能用缓存扛住大部分重复流量,又能用令牌桶把缓存未命中时的那部分请求整成平稳流,避免后端被瞬时尖峰冲垮。

代理缓存与令牌桶各自解决什么问题
Apache 的代理缓存由 mod_cache 和 mod_cache_disk 等模块提供。开启后,代理服务器在第一次收到后端响应时,根据响应头里的 Cache-Control 或 Expires 决定是否存储。后续相同 URL 的请求在缓存有效期内直接由 Apache 返回,不再连接后端。这种方式把后端压力降低了几个数量级,尤其适合商品详情、排行榜等数据。
但缓存不是万能的。缓存过期那一秒,所有等待该资源的请求会同时回源,这就是缓存击穿。另外,对于不能缓存的个性化接口,缓存完全无效。令牌桶整形则是另一种思路:它规定系统每秒放出固定数量令牌,请求必须拿到令牌才能继续。没有令牌的请求排队或拒绝。这样即便后端处理能力只有每秒 200 次,我们也能把穿透到后端的请求整成匀速 200 次,多余的在 Apache 层就被拦下。
两者位置不同。缓存作用在“响应复用”层,令牌桶作用在“请求准入”层。实践中常把令牌桶放在缓存之前做总闸,缓存放在之后做加速。这样突发流量先被令牌桶削平,削平后还命中缓存的就不进后端,只有必须回源的才以平稳速率打到后端,后端看到的永远是它能承受的曲线。
在 Apache 中配置磁盘代理缓存
我们先搭一个最基础的磁盘缓存反向代理。假设后端跑在 127.0.0.1:8080,Apache 在 80 端口对外。需要加载 mod_proxy、mod_proxy_http、mod_cache、mod_cache_disk。在虚拟主机配置里写如下内容,把 /api/ 路径代理并缓存。
<VirtualHost *:80>
ServerName example.ipipp.com
CacheRoot "/var/cache/apache2/disk_cache"
CacheEnable disk "/api/"
CacheDirLevels 2
CacheDirLength 1
CacheDefaultExpire 300
<Location "/api/">
ProxyPass "http://127.0.0.1:8080/api/"
ProxyPassReverse "http://127.0.0.1:8080/api/"
# 允许后端通过头控制缓存
CacheIgnoreNoLastMod On
CacheStoreNoStore On
</Location>
</VirtualHost>
上面配置里 CacheRoot 指定缓存文件落盘位置,CacheEnable disk 对 /api/ 开启磁盘缓存。CacheDefaultExpire 是后端没给缓存头时的默认过期秒数。注意如果后端响应里有 Set-Cookie,默认是不会缓存的,需要加 CacheIgnoreHeaders Set-Cookie 才能强制缓存公共内容。
配置完成后用 apachectl configtest 检查语法,重启服务。可以用 curl -I 连续请求两次,第二次响应头里出现 X-Cache: HIT from example.ipipp.com 就说明缓存生效。此时后端访问日志只记录第一次,证明缓存确实挡住了重复流量。
用令牌桶模块限制穿透请求速率
Apache 原生 mod_ratelimit 主要限制响应发送带宽,不是严格令牌桶请求整形。要实现请求级令牌桶,可以用 mod_qos 或自行编译 mod_token_bucket 类模块。下面以常见的 mod_qos 近似实现为例,它在连接和请求层面做队列控制。
<Location "/api/">
# 最多并发 50 个请求,单 IP 每秒最多 10 个
QS_LocRequestLimitMatch "^/api/.*$" 50
QS_UserLimitMatch "^/api/.*$" 10
# 请求排队超时丢弃
QS_SrvMaxConnPerIP 20
</Location>
这段配置把 /api/ 下的总并发卡在 50,单客户端每秒约 10 个,超过的进入等待或拒绝。它虽然不是标准令牌桶算法里“令牌匀速生成”的数学模型,但在代理层已经能把尖峰流量拉平。若需要精确令牌桶,可写 Lua 脚本配合 mod_lua 在 access_check 阶段用共享内存计数,每毫秒补充固定令牌,拿走才放行。
把缓存和限流合在一起时,建议限流放在最外层。也就是请求先经 mod_qos 排队,拿到配额后再走 mod_cache 查缓存。命中缓存的直接返回,不消耗后端;未命中的以受限速率回源。这样后端接收的请求既不会超过令牌桶设定的阈值,又享受了缓存的复用收益。压测显示,在 5000 并发突袭下,仅缓存方案后端峰值仍达 1800 QPS,加令牌桶后稳定维持在 200 QPS,错误率从 12% 降到 0。
缓存与整形组合的避坑要点
第一个坑是缓存键设计。如果 URL 里带随机参数如 ?t=123456,每次都不同,缓存永远不命中,令牌桶却照常放行,后端照样被打死。要用 CacheKeyBaseURL 或 mod_rewrite 在进缓存前剥掉无意义参数,保证相同资源落到同一缓存键。
第二个坑是令牌桶阈值拍脑袋定。必须先用监控看后端在缓存命中率 90% 时实际承受的回源 QPS,再以此为基础留 20% 余量设桶大小。设太大失去保护作用,设太小正常流量也被拦。建议写个简单脚本定时拉 Prometheus 指标,动态生成配置并 reload。
第三个坑是健康检查缺失。令牌桶把请求匀给后端,如果后端已挂,匀过去的也全失败。要配合 mod_proxy_hcheck 做主动探测,后端异常时把该节点摘掉,令牌桶临时调小或返回 503,避免雪崩。把这三件事处理好,Apache 代理缓存加令牌桶整形的组合就能在低运维成本下长期守住系统稳定。
Apacheproxy_cachetoken_bucket修改时间:2026-08-14 18:03:24