Apache代理缓存的空间回收并不是单一组件提供的统一GC机制。以常见的Apache HTTP Server为例,mod_cache_disk负责把响应头和响应体写入磁盘,但它不会在后台主动扫描并删除旧文件。所谓空间回收,主要通过htcacheclean工具按照目录总容量或文件访问时间进行清理。Apache Traffic Server则不同,它把缓存存储在多个独立卷中,由专门的GC线程周期性扫描并淘汰低价值对象。明确这些差异后,可以根据实际部署选择对应的回收策略。

触发条件:Apache HTTP Server为什么需要外部工具回收
mod_cache_disk的写入流程决定了它不能像内存缓存那样随时释放空间。当反向代理收到源站响应后,mod_cache会根据响应头判断是否可缓存,例如Cache-Control、Expires、Set-Cookie等。如果可以缓存,它会先生成缓存键,再根据CacheRoot、CacheDirLevels和CacheDirLength计算出目标路径。缓存键通常是对URL和请求头做哈希得到的十六进制字符串,目录层级用于避免单个目录下文件过多。这个写入过程是请求驱动的,也就是说只有访问对应资源时才可能产生缓存文件。
问题在于,过期对象的删除并不发生在写入阶段。mod_cache_disk只负责存储和读取,即使缓存对象已经超过CacheMaxExpire,也不会立刻从磁盘删除。只有后续请求命中该缓存时,Apache才会发现对象过期,并尝试向源站重新验证或获取新内容。如果源站不可达且允许提供过期内容,旧文件还会继续被复用。因此单纯依赖mod_cache_disk,磁盘空间会持续增长,直到文件系统写满。这正是需要htcacheclean做独立回收的原因。
触发回收通常有两种方式:一种是手动执行htcacheclean命令,适合临时清理或脚本调用;另一种是让htcacheclean以守护进程模式运行,持续监控缓存目录总大小。无论哪种方式,核心逻辑都是扫描CacheRoot下的所有子目录,按照文件访问时间或大小排序,从最旧或最大的对象开始删除,直到低于设定的容量上限。
下面的配置启用了磁盘缓存,并限制单文件最大5MB,避免大响应对象迅速占满空间:
CacheRoot /var/cache/apache2/mod_cache_disk CacheDirLevels 2 CacheDirLength 1 CacheMaxFileSize 5000000 CacheMinFileSize 1 CacheDefaultExpire 3600 CacheMaxExpire 86400
htcacheclean的清理模式与目录结构
htcacheclean是Apache HTTP Server自带的缓存清理工具,通常位于安装目录的bin子目录下。它不依赖mod_cache运行,可以独立扫描磁盘上的缓存目录。最常用的参数是-p指定CacheRoot路径,-l指定目标总大小。例如要把缓存目录控制在2GB以内,可以使用如下命令:
htcacheclean -d -p /var/cache/apache2/mod_cache_disk -l 2048M -t
这里的-d表示守护进程模式,工具会驻留后台并周期性检查目录大小;-l后面的2048M是容量上限;-t表示清理完成后删除空目录。需要注意的是,htcacheclean删除文件时并不解析缓存对象的HTTP头,而是基于文件系统的元数据,比如最近修改时间、文件大小和目录总占用。这意味着一个刚刚写入但尚未过期的大文件,也可能因为总容量超限而被删除。对于希望优先保留热点对象的场景,这种粗粒度清理并不理想。
缓存目录结构对清理效率影响很大。CacheDirLevels和CacheDirLength共同决定目录深度和每级目录名长度。例如CacheDirLevels 2、CacheDirLength 1会生成类似/var/cache/apache2/mod_cache_disk/a/b/cachekey.header这样的路径。当层级过浅时,单个目录可能堆积大量文件,导致htcacheclean扫描变慢;层级过深则会产生大量小目录,清理空目录的开销增加。一般建议根据缓存条目总数调整,条目较多时增加CacheDirLevels到3,并保持CacheDirLength为1或2。
除了容量限制,htcacheclean还可以配合系统计划任务定期全量清理。很多生产环境会在凌晨低峰期执行一次清理,避免白天影响正常请求。以下cron配置每天3点执行一次清理,将缓存限制在4GB:
0 3 * * * /usr/bin/htcacheclean -p /var/cache/apache2/mod_cache_disk -l 4096M -t
不带守护参数时,htcacheclean执行完一次清理后自动退出,更适合cron场景。如果缓存增长很快,单次清理可能不够,此时建议同时运行守护进程模式,并设置较小的检查间隔。不过htcacheclean的守护模式只负责空间回收,不会主动刷新缓存统计信息,实际容量变化需要通过文件系统查看。
Apache Traffic Server的缓存GC机制
与Apache HTTP Server不同,Apache Traffic Server是一个专门的代理缓存服务器,内置了完整的缓存存储和GC机制。它的缓存空间被划分为多个volume,每个volume对应磁盘上的一个目录,可以单独设置大小和策略。ATS的GC线程会按照固定间隔扫描这些volume,统计当前使用量,并决定是否需要回收。当某个volume的已用空间接近上限时,GC会开始淘汰对象,释放空间给新缓存写入。
ATS的淘汰策略比htcacheclean更精细。它不只看文件时间,还会考虑对象的访问频率、大小和存储成本。一些配置项可以控制GC的激进程度,例如限制单个对象最大尺寸、设置缓存目标水位和扫描间隔。合理配置这些参数可以避免GC频繁运行导致磁盘IO升高,也能防止缓存空间长期处于高位。以下是一个records.config中的示例片段:
CONFIG proxy.config.cache.gc_interval INT 600 CONFIG proxy.config.cache.limits.http.max_size INT 1073741824 CONFIG proxy.config.cache.target_fragment_size INT 1048576
其中gc_interval控制GC扫描间隔,单位是秒;limits.http.max_size限制单个HTTP对象最大1GB;target_fragment_size影响存储分片大小。ATS会根据这些参数和实际请求模式动态调整回收节奏。如果发现缓存命中率下降或磁盘IO异常,可以检查GC日志和存储卷使用率,适当放宽容量或降低扫描频率。
实战配置建议:避免磁盘写满导致代理不可用
无论使用Apache HTTP Server还是ATS,都应从三个方面控制缓存空间。第一是限制可缓存对象的大小和过期时间,避免大文件或无过期时间的响应长期占用磁盘。第二是设置容量上限并启用清理工具或GC线程,保证空间不足时能自动回收。第三是监控磁盘使用率,当剩余空间低于阈值时提前告警,而不是等到写满后再处理。
对于Apache HTTP Server,完整的反向代理缓存配置可以这样写:
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
<IfModule mod_cache.c>
CacheEnable disk /
CacheRoot /var/cache/apache2/mod_cache_disk
CacheDirLevels 2
CacheDirLength 1
CacheDefaultExpire 3600
CacheMaxExpire 86400
CacheMaxFileSize 5000000
CacheIgnoreNoLastMod On
</IfModule>
这个配置中,CacheEnable disk /表示对根路径下的所有请求启用磁盘缓存。CacheMaxFileSize限制单个缓存对象最大5MB,CacheDefaultExpire和CacheMaxExpire控制过期范围。CacheIgnoreNoLastMod用于处理没有Last-Modified头的响应,避免部分资源无法缓存。配合前面提到的htcacheclean守护进程或cron任务,就能形成基本的空间回收闭环。
如果使用Windows环境运行Apache,htcacheclean命令通常位于C:\Apache24\bin\htcacheclean.exe,缓存目录可能配置为C:\Apache24\cache\mod_cache_disk。在计划任务中引用时要注意路径反斜杠不能省略,例如:
C:\Apache24\bin\htcacheclean.exe -p C:\Apache24\cache\mod_cache_disk -l 2048M -t
反斜杠是Windows路径的一部分,部分脚本或配置解析器可能会把反斜杠当转义符处理,因此在实际批处理中建议使用引号包裹路径,避免路径中的空格和特殊字符造成问题。对于ATS用户,则需要分别检查storage.config中的卷划分和records.config中的GC参数,确保每个卷都预留至少10%到20%的剩余空间,避免GC来不及回收时写入失败。

