目前有很多速率限制和 QoS 工具,但到目前为止我还没有找到一个能够满足我的特定需求的工具,如下所示:
我有一个在 Apache 2.2 上运行的 Web 应用程序,我希望每个客户每天的请求数被限制为 10000 个。限制不应基于客户的 IP 地址,而应基于客户的帐户,这可以从 URL 中提取出来;例如:http://mywebsite.com/customer1/page1。一旦超出限制,我希望将来自它们的请求“减慢”到某个预定义值,例如每分钟 15 个请求,也许可以通过引入延迟来实现。如果这不可能,那么返回 503 Service Unavailable 之类的错误就可以了。
我该如何使用 Apache 来实现这一点?如果没有办法使用 Apache 来实现这一点,那么我很乐意听取有关其他工具、反向代理等的建议。
更新:速率限制需要跨多个服务(mod_dav_svn,mod_passenger,mod_wsgi)进行,并且必须具有高性能。
答案1
我认为您无法仅使用 Apache 来做到这一点。
有很多执行带宽限制的 Apache 模块但我认为它们中没有一个符合你所描述的。
mod_bw可以根据请求的各个部分来限制带宽。它可以根据 IP 地址、文件扩展名、mime 类型、文件大小和目录进行限制(根据我对文档)。您可以通过将模块的指令放在块内来实现基于目录的限制<Directory >
,因此我希望它们在块内也能同样正常工作<Location >
。mod_bwshare似乎进入了里面<Directory >
并且<Location >
也被阻挡了。
这些 Apache 模块的配置进入您的 Apache 配置,它们不会从任何外部源查找值,这意味着您必须重新启动 Apache 才能更改任何值。如果您只想在客户超出配额时限制带宽,则需要编辑 Apache 配置文件,并在每次客户这样做时重新启动 Apache,并在配额使用到期时再次执行此操作。
我这样做的方法是,用您使用的任何语言编写一个小应用程序,接受文件请求,检查数据库中的客户是否超出配额,然后以适当的速率向他们发送文件。编写 mod_bw 的人解释了他如何实现节流:他只是将文件分成小的“块”(例如每个块 5KB),然后在将sleep()
每个块回显到客户端之间进行短暂的切换。
然后我会用mod_rewrite
make 将原始文件请求(您说的看起来像/customer1/page1
)转换成类似的内容/throttle.php?customer=customer1&file=page1
。
应用程序可以在下载开始时写入数据库,以表明它目前正在处理文件,并在下载结束时再次写入数据库,以表明它已完成。这应该可以让你阻止一个客户独占你所有的 Apache 子进程。
答案2
我刚刚接受了@Ladadadada 的回答“Apache 无法解决这个问题”,但我想列出一些我正在寻找的解决这个问题的方法,以供参考:
编写我自己的 Apache 模块。它将使用共享内存来跟踪所有请求以及它们要发送到哪些帐户。这些计数将在 24 小时后过期。超过下限的帐户的每个请求都会延迟一段时间,而超过上限的帐户将对所有请求返回 503 状态。将使用 SetEnvIf 从 URL 中提取帐户名称。
编写一个脚本(使用 Ruby),该脚本将作为守护进程运行并跟踪 Apache 访问日志。它将从每个记录的请求中提取帐户名称,并像 #1 一样跟踪它们,而无需共享内存的复杂性。它还将跟踪过去 24 小时内与给定帐户相关联的所有 IP。当帐户超出其限制时,它将为与该帐户相关联的每个 IP 添加 NFQUEUE iptables 规则。来自这些 IP 的数据包将由 Ruby 脚本使用 Netfilter::Queue(在第二个线程中)捕获,它们将被延迟。