Apache作为最流行的开源Web服务器之一,除了提供静态内容服务,还经常被当作反向代理和缓存网关使用。当后端有多个服务节点,或者前端有大量并发请求经过代理转发时,调度策略的选择就直接决定了整体服务质量。加权公平队列,也就是常说的WFQ,Weighted Fair Queuing,是一种按权重比例分配带宽和转发机会的算法,它能有效避免某些大流量请求把其他请求挤掉的问题。本文将围绕Apache代理缓存场景,详细讲解WFQ的原理、配置方法和调优经验。

一、WFQ加权公平队列的核心原理
WFQ的思想源自网络流量调度领域。传统的时间片轮询对每个队列一视同仁,但如果某个队列的流量是其他队列的十倍,它实际需要的服务能力也完全不同。WFQ给每个队列分配一个权重值,调度时不是简单轮流,而是按照权重的比例来分配转发机会。比如队列A权重为3,队列B权重为1,那么长期来看,队列A获得的带宽大约是队列B的三倍。
WFQ的实现关键在于虚拟时钟机制。每个数据包到达队列时,系统会计算一个虚拟完成时间,公式通常是:完成时间等于当前虚拟时钟加上包大小除以权重。调度器总是优先发送虚拟完成时间最小的包。这样做的好处是,小包不会被大包长时间阻塞,权重低的队列虽然分到的带宽少,但永远不会被完全饿死,这正是公平二字的含义。
在Apache代理缓存的场景下,WFQ的思想体现在两个方面。第一是上游转发层面,mod_proxy对后端节点加权,让性能强的节点承担更多请求;第二是缓存服务层面,对不同类别的内容、不同优先级的请求队列进行加权调度,避免缓存失效风暴或大文件回源时把代理线程池全部占满。理解了这两个层面,后续的配置才有明确的落脚点。
二、在Apache中配置代理缓存与加权负载均衡
Apache通过mod_proxy、mod_proxy_balancer和mod_cache这三个模块的组合,可以实现代理缓存与后端加权调度。mod_proxy_balancer本身支持三种调度算法:轮询、请求数计数和流量计数,虽然没有直接叫WFQ,但加权流量模式的行为与WFQ的思想高度一致,都是按权重和服务量综合分配。下面给出一个完整的配置示例。
# 加载所需模块
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule cache_module modules/mod_cache.so
LoadModule cache_disk_module modules/mod_cache_disk.so
<IfModule mod_proxy_balancer>
# 定义后端集群,lbmethod=bytraffic 表示按流量计数调度
<Proxy balancer://backend_pool>
BalancerMember http://192.168.1.101:8080 loadfactor=3 retry=10
BalancerMember http://192.168.1.102:8080 loadfactor=2 retry=10
BalancerMember http://192.168.1.103:8080 loadfactor=1 retry=10
ProxySet lbmethod=bytraffic
ProxySet stickysession=ROUTEID
</Proxy>
</IfModule>
# 磁盘缓存配置
CacheEnable disk /
CacheRoot "/var/cache/apache2/proxy"
CacheDirLevels 2
CacheDirLength 1
CacheMaxFileSize 10000000
CacheMinFileSize 100
# 代理转发入口
<VirtualHost *:80>
ServerName proxy.ipipp.com
ProxyPass "/" "balancer://backend_pool/"
ProxyPassReverse "/" "balancer://backend_pool/"
CacheDefaultExpire 3600
</VirtualHost>
配置中的loadfactor就是权重参数,取值范围1到100。上面三台后端节点权重比为3:2:1,使用lbmethod=bytraffic后,Apache会统计每个成员实际传输的流量,按权重比例动态调整转发目标,这与WFQ按服务量加权的行为一致。retry=10表示节点故障后10秒内不再尝试,起到快速失败保护的作用。
缓存部分需要注意几个细节。CacheMaxFileSize限制单个缓存对象的大小,超过10MB的响应直接透传不落盘,避免大文件把磁盘缓存塞满;CacheDirLevels和CacheDirLength控制缓存目录的层级结构,文件数量大时建议层级设为2或3,否则单目录下文件过多会导致文件系统性能下降。另外,如果后端响应头中没有Expires或Cache-Control,可以使用CacheDefaultExpire设定默认过期时间。
三、WFQ与其他调度策略的对比与选择
选择调度策略前,先弄清各方案的特点。下表对比了Apache中常见的几种调度方式。
| 调度方式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| byrequests(轮询) | 按请求数轮流分配,可加权 | 配置简单,开销小 | 忽略请求之间的耗时差异,长请求会拖累后端 |
| bytraffic(按流量) | 按传输字节数加权调度 | 接近WFQ行为,对带宽敏感,适合下载类服务 | 需要统计流量,有轻微计算开销 |
| bybusyness(最少忙碌) | 优先分给当前活跃请求数最少的节点 | 自适应能力强,处理耗时参差的请求效果好 | 突发流量下分配可能不均 |
如果后端提供的内容以小文件、短响应为主,请求耗时差异不大,byrequests加权重就能满足需求;如果服务包含大量图片、视频或文件下载,流量差异显著,bytraffic是更合适的选择,它让权重真正对应带宽占比;如果每个请求的处理时间波动很大,比如动态接口响应时间从几十毫秒到几秒不等,bybusyness往往表现更稳定。
还有一个容易被忽视的问题是缓存失效风暴。当一批热点内容同时过期,大量回源请求会瞬间打到后端,即使有加权调度,也可能压垮权重最高的节点。解决办法是开启CacheStaleOnError和CacheLock相关机制,让同一资源的回源请求合并处理,配合较长的软过期时间,可以显著平滑回源峰值。
四、生产环境调优经验与常见坑
第一,权重不是拍脑袋定的。建议先观察各后端节点的实际处理能力,可以用mod_status配合balancer-manager页面观察每个成员的负载和错误率,再按能力比例设置loadfactor。如果权重设置与实际能力偏差过大,会出现强节点空闲、弱节点过载的怪象。
第二,注意缓存与代理调度的相互作用。命中缓存的请求根本不会转发到后端,所以缓存的引入实际上降低了调度压力。合理提高缓存命中率,比单纯调整权重更能改善整体吞吐。可以通过日志中的%{cache-status}n变量统计命中情况,命中率长期偏低的路径要检查Cache-Control头是否合理。
# 在日志中记录缓存命中状态
LogFormat "%h %l %u %t \"%r\" %>s %b cache:%{cache-status}n" proxylog
CustomLog "logs/proxy_access.log" proxylog
第三,安全方面务必收紧balancer-manager。这个管理页面可以在线调整权重甚至禁用节点,一旦暴露给外网风险极大,建议只允许内网IP访问,或者干脆用基本认证再加一层保护。第四,超时参数要配套设置,ProxyTimeout、后端timeout与缓存读取超时要协调一致,避免后端已经超时释放,而代理线程还挂在等待状态,白白消耗连接资源。
总结一下,WFQ的精髓在于按权重比例分配服务机会,同时保证任何队列不会被饿死。在Apache中,通过mod_proxy_balancer的bytraffic加权模式配合mod_cache,可以搭建出一套公平且高效的代理缓存架构。实际落地时,权重设定要基于真实负载观测,缓存策略要服务于减少回源,再加上完善的日志监控,这套方案完全能够支撑中大型站点的流量调度需求。
Apache代理缓存加权公平队列WFQ修改时间:2026-09-03 21:03:12