我使用 HaProxy 实现了简单的速率限制,方式与 StackExchange 类似可以使用 HaProxy。我正在尝试使其更先进一些,以便有多个速率限制阈值。
例如,限制客户端的请求:
15/分钟
60 /小时
360/天
似乎我需要多个 stick-tables 来存储具有不同采样率的相同数据。文档指出:
每个代理只有一个 stick-table。在撰写本文档时,每个代理有多个表似乎没什么用。如果需要,只需创建一个包含 stick-table 的虚拟后端并引用它即可。
不幸的是,我花了很长时间才弄清楚如何将数据存储到虚拟后端表中。
我也愿意尝试其他方法,HaProxy 看起来是一条很有前途的道路,而且我们已经在环境中使用了它,所以它很有意义。任何建议都值得赞赏。
答案1
我只是想自己做这件事,但没有成功,于是决定求助于我的谷歌搜索。当我寻找多级速率限制时,得到的最佳结果就是这个,我真的很兴奋。然后我发现它没有答案,最初陷入了绝望的深渊。在摆脱困境后,我继续破解,幸运的是,我似乎找到了如何做到这一点的方法,至少可以满足我的需要。也许它也适合你。
Haproxy 真的非常酷,我很高兴开始使用它来代替我们当前的负载平衡解决方案,但 stick-tables 有点难以理解。在这方面,我发现一个似乎对我有帮助的一般原则,那就是明确引用每一个当您尝试设置多个 stick 表时,按名称来设置 stick 表。默认行为(即名称是隐式的(假定是您所在的后端))很棒……除非您开始尝试使用多个 stick 表。因此,这就是为什么在我下面的配置中,其中一些比它必须的更冗长。我只是觉得这样更容易遵循逻辑。无论如何,开始吧(请注意,这是基于 Moodle 应用程序的 cookie 而不是 IP 进行计数,并且它使用的是 haproxy 的 v1.5.11):
backend dynamic_60
stick-table type string len 36 size 1m store gpc0_rate(60s)
backend dynamic
stick-table type string len 36 size 1m store gpc0_rate(10s)
stick on cookie(MoodleSession) table dynamic
stick on cookie(MoodleSession) table dynamic_60
tcp-request content track-sc0 cookie(MoodleSession) table dynamic
tcp-request content track-sc1 cookie(MoodleSession) table dynamic_60
acl rate_10s sc0_inc_gpc0(dynamic) gt 0
acl rate_60s sc1_inc_gpc0(dynamic_60) gt 0
tcp-request content reject if rate_10s rate_60s FALSE
因此,它的作用是设置一个计数器来记录每 10 秒的速率,另一个计数器来记录每 60 秒的速率。请注意,它实际上尚未使用这些计数器进行任何速率限制。但您可以通过以下方式进行验证:
echo "show table dynamic" | socat /var/run/haproxy/admin.sock stdio
echo "show table dynamic_60" | socat /var/run/haproxy/admin.sock stdio
汇率计数器是单独维护的。
我想找出使这些计数器实际递增所需的最小配置,这就是为什么您会在“tcp-request content rejection”语句末尾看到“FALSE”。仅使用计数器定义 acl 不会使它们递增。您必须实际使用 acl。在末尾加上“FALSE”只是允许我使用 acl,而无需满足实际拒绝请求的条件。一旦我决定了这些 acl 的一些实际数字,我可能会删除“FALSE”。
让多个 stick 表工作的真正关键似乎是在实际处理请求的后端使用“sc{0,1,2}_inc_gpc0”执行“stick on”、“track-sc{0|1|2}”和 acl 定义。将其中任何一个移动到 dynamic_60 后端都会导致计数停止工作。我猜原因是跟踪或将 acl 应用于不提供请求服务的后端是没有意义的,因为它实际上没有请求来从中提取信息。话虽如此,我相信其他人会有更好的解释。我对 haproxy 还很陌生。
我问的下一个问题是:我是否只能跟踪 3 件事(因为“track-sc”配置设置只能从 0 到 2)。我相信,是的,你只能跟踪这三件事。但重要的是,这是 3 件事每后端实际上为请求提供服务。因此,例如,如果您像我一样想要对静态内容和动态内容进行不同的速率限制,您可以根据请求中的某些内容决定在前端使用“静态”还是“动态”后端。然后在“静态”后端,在“静态”和“static_60”后端上定义 track-sc0 和 track-sc1(如果您恰好遵循与我上面放置的配置类似的命名方案)。然后,您将有 4 个 stick 表可用于做出速率限制决策。动态和静态内容的 10s 和 60s 速率。使用第三个计数器,我认为您可以获得 3 个级别,但我认为这是极限。
答案2
我也花了一段时间来研究这个问题。David Ackerman 的解决方案效果很好,但如果您只需要两个限制,可以使用第二个通用寄存器(自 HAProxy 1.9+ 开始可用)来简化。这是我限制每分钟和每天请求数的解决方案。
acl exceeds_limit_minute sc_gpc0_rate(0) ge 50
acl exceeds_limit_day sc_gpc1_rate(0) ge 18000
stick-table type ip size 100k expire 24hs store gpc0,gpc0_rate(60s),gpc1,gpc1_rate(24h)
http-request track-sc0 src
http-request sc-inc-gpc0(0) unless exceeds_limit_minute or exceeds_limit_day
http-request sc-inc-gpc1(0) unless exceeds_limit_minute or exceeds_limit_day
http-request deny deny_status 429 if exceeds_limit_minute or exceeds_limit_day
此解决方案也不计算被阻止的请求