之前的blog中提到了HAProxy中的gpc0这个计数器,当时理解的不是很清楚。
后来又google了一下,找到了一篇相当有用的文档,我把我的理解写下来,希望对大家有点用处。

我们来看如下一个示例配置:

001  global
002    log 127.0.0.1   local0
003    log 127.0.0.1   local1 notice
004    stats socket /var/run/haproxy.stat mode 600 level operator
005    maxconn 4096
006    user haproxy
007    group haproxy
010    daemon
011 
012  defaults
013    log global
014    mode http
015    option httplog
016    option dontlognull
017    retries 3
020    option redispatch
021    maxconn 2000
022    contimeout 5000
023    clitimeout 50000
024    srvtimeout 50000
025 
026  frontend http
027    bind *:2550
030    stick-table type ip size 200k expire 10m store gpc0
031    # check the source before tracking counters, that will allow it to
032    # expire the entry even if there is still activity.
033    acl whitelist src 192.168.1.154
034    acl source_is_abuser src_get_gpc0(http) gt 0
035    use_backend ease-up-y0 if source_is_abuser
036    tcp-request connection track-sc1 src if !source_is_abuser
037 
040    acl is_test1 hdr_sub(host) -i test1.com
041    acl is_test2 hdr_sub(host) -i test2.com
042 
043    use_backend test1 if is_test1
044    use_backend test2 if is_test2
045 
046  backend test1
047    stick-table type ip size 200k expire 30s store conn_rate(100s),bytes_out_rate(60s)
050    acl whitelist src 192.168.1.154
051    # values below are specific to the backend
052    tcp-request content track-sc2 src
053    acl conn_rate_abuse sc2_conn_rate gt 3
054    acl data_rate_abuse sc2_bytes_out_rate  gt 20000000
055 
056    # abuse is marked in the frontend so that it's shared between all sites
057    acl mark_as_abuser sc1_inc_gpc0 gt 0
060    tcp-request content reject if conn_rate_abuse !whitelist mark_as_abuser
061    tcp-request content reject if data_rate_abuse mark_as_abuser
062    server local_apache localhost:80
063 
064  backend test2
065    stick-table type ip size 200k expire 1m store conn_rate(100s),bytes_out_rate(60s)
066    acl whitelist src 192.168.1.154
067    # values below are specific to the backend
070    tcp-request content track-sc2 src
071    acl conn_rate_abuse sc2_conn_rate gt 5
072    acl data_rate_abuse sc2_bytes_out_rate gt 20000000
073 
074    # abuse is marked in the frontend so that it's shared between all sites
075    acl mark_as_abuser sc1_inc_gpc0 gt 0
076    tcp-request content reject if conn_rate_abuse !whitelist mark_as_abuser
077    tcp-request content reject if data_rate_abuse mark_as_abuser
100    server local_apache localhost:80
101 
102  backend ease-up-y0
103     mode http
104     errorfile 503 /etc/haproxy/errors/503rate.http

整个配置实现如下目的:
1.根据主机头判断是test1.com还是test2.com,然后发到对应的backend服务器
2.来自192.168.1.154的访问不受访问速率限制
3.如果100秒之内平均连接速率或者发送的字节数速率超过限制,则使用ease-up-y0这个backend处理,将持续返回503错误给用户。

下边来解说一下我们关注的限速部分的配置,我将按照逻辑的顺序说明,而不是配置文件中的位置。

1.针对backend test1,我们定义一个stick-table,key类型是ip地址,表项最多200K个,表项自动过期时间3分钟,存储2个数据: 100秒内本IP新建connection的速率,60秒内本IP向外发送数据大小的比率

047  stick-table type ip size 200k expire 3m store conn_rate(100s),bytes_out_rate(60s)

stick-table中每个IP表项有50bytes大小,加上connection rate和bytes out rate各12bytes。
所以每个表项占用74bytes内存,742001024/1024*1024 ~= 14.11MBytes
所以整个stick-table占用14Mbytes内存。

  1. 将请求的源ip地址作为key,存入当前stick-table的sc2计数器内(官方的建议是sc0用于frontend,sc1用于backend)
052    tcp-request content track-sc2 src

如果100秒内新建连接速率大于3,则标记acl conn_rate_abuse
如果60秒内发送数据的数量大于20000000 bytes,则标记acl data_rate_abuse

053    acl conn_rate_abuse sc2_conn_rate gt 3
054    acl data_rate_abuse sc2_bytes_out_rate gt 20000000

3.接下来这段真正执行这些规则。

057    acl mark_as_abuser sc1_inc_gpc0 gt 0
060    tcp-request content reject if conn_rate_abuse !whitelist mark_as_abuser
061    tcp-request content reject if data_rate_abuse mark_as_abuser

这段配置比较晦涩难懂,但并不复杂。
gpc0是一个通用计数器,sc1_inc_gpc0是gpc0计数器增量(我们在frontend配置中使用)

060    tcp-request content reject if conn_rate_abuse !whitelist mark_as_abuser

这一行说明,无论用户违反了连接速率或者发送byte速率的限制,而且用户不在白名单中,则frontend中的gpc0计数器的值会增加。
sc1_inc_gpc0永远返回真值,根据短路求值的原则,如果conn_rate_abuse或data_rate_abuse不为真值,则mark_as_abuser规则不会被触发执行。

4.上边的gpc0计数器,会在frontend中被处理

030    stick-table type ip size 200k expire 10m store gpc0
031    # check the source before tracking counters, that will allow it to
032    # expire the entry even if there is still activity.
033    acl whitelist src 192.168.1.154
034    acl source_is_abuser src_get_gpc0(http) gt 0
035    use_backend ease-up-y0 if source_is_abuser
036    tcp-request connection track-sc1 src if !source_is_abuser

在frontend我们声明了一个stick-table,用于存储per source ip的通用计数器。如果这个计数器的值被backend增加了(大于0)
则使用ease-up-y0这个backend服务器,用户会被返回一个503错误页面。
一旦用户触发了规则,则这个source ip将不会再被跟踪,也就是说这个source ip会一直被返回503错误。
直到这个source ip表项过期,本例中是10分钟(expire 10m)。

参考文档:
http://blog.serverfault.com/2010/08/26/1016491873/

文档更新时间: 2020-02-08 12:32   作者:月影鹏鹏