haproxy
基于haproxy实现的滑动计数示例
haproxy slide window
代码结构
(.venv) ryefccd@republic:~/workspace/brde$ tree -L 2 conf/haproxy/
conf/haproxy/
├── haproxy.cfg # haproxy 配置文件
├── haproxy_demo.cfg # haproxy slide window 配置文件
├── readme.md # 自述文件
└── sw_openapi # haproxy 导出的 restful 接口的 openapi 文档
├── swagger-ui # openapi 接口结构页面渲染js和css依赖文件
├── sw_func.html # openapi(swagger) 页面入口
└── sw_func.json # restful 接口的spec定义文件
2 directory, 5 files
部署说明
将 haproxy.cfg 文件 和 sw_openapi 文件夹复制到 /etc/haproxy 文件夹中.
根据实际情况修改 haproxy 的监听端口(bind :port).
frontend fe_api
bind :88
use_backend rate_10s if { path /rate_10s } #### \{ 与 path 之间要空格, uri 与 \} 之间也要保空格
use_backend rate_1m if { path /rate_1m }
use_backend rate_5m if { path /rate_5m }
use_backend rate_1h if { path /rate_1h }
use_backend rate_1d if { path /rate_1d }
use_backend rate_7d if { path /rate_7d }
use_backend group_distinct_1m if { path /group_distinct_1m }
use_backend group_distinct_5m if { path /group_distinct_5m }
use_backend group_distinct_1h if { path /group_distinct_1h }
use_backend group_distinct_1d if { path /group_distinct_1d }
use_backend group_distinct_7d if { path /group_distinct_7d }
...
最后结构如下所示:
root@ub20:~# tree -L 2 /etc/haproxy/
/etc/haproxy/
├── errors
│ ├── 400.http
│ ├── 403.http
│ ├── 408.http
│ ├── 500.http
│ ├── 502.http
│ ├── 503.http
│ └── 504.http
├── haproxy.cfg
└── sw_openapi
├── sw_func.html
├── sw_func.json
└── swagger-ui
最后执行 systemctl reload haproxy
即可重载服务.
测试步骤
测试脚本:
# 在最近10s的窗口移动步长, 30s窗口长度之内做 fccdabc 值进行频率计数
curl "http://127.0.0.1/rate_10s?v=fccdabc"
# 在最近1m的窗口移动步长, 3m窗口长度之内做 fccdabc 值进行频率计数
curl "http://127.0.0.1/rate_1m?v=fccdabc"
# 在最近1h的窗口移动步长, 3h窗口长度之内做 fccdabc 值进行频率计数
curl "http://127.0.0.1/rate_1h?v=fccdabc"
# 对mykey进行查看, 查看当前的剩余过期时间ttl和上次计数修改时间
curl -i -v -XGET "http://127.0.0.1/?v=fccdabc"
以后可以在修改数据前使用 haproxy 的变量把ttl和上次计数修改时间记录下来. 最后和当前的最新计数返回. 这些信息有助于去记录数据的分布.
压测命令:
wrk -t12 -c400 -d30s --latency "http://10.84.71.214/rate_10s?v=fccdabc"
# 对照组
wrk -t12 -c400 -d30s --latency "http://10.84.71.214/?v=fccdabc"
调试 stick table:
echo "show table rate_10s" | socat unix:/run/haproxy/admin.sock -
每隔一秒刷新 stick table 中的数据.
watch -n 1 'echo "show table rate_10s" | socat unix:/run/haproxy/admin.sock -'
haproxy distinct count ratelimit
测试脚本:
curl "http://150.158.144.155:88/group_distinct_1m?group=deviced1&v=fccd1"
...
curl "http://150.158.144.155:88/group_distinct_1m?group=deviced1&v=fccd2"
...
curl "http://150.158.144.155:88/group_distinct_1m?group=deviced1&v=fccd3"
...
curl "http://150.158.144.155:88/group_distinct_1m?group=deviced1&v=fccd4"
...
curl "http://150.158.144.155:88/group_distinct_1m?group=deviced1&v=fccd5"
...
watch -n 1 'echo "show table group_distinct_1m" | sudo socat unix:/run/haproxy/admin.sock -'
Every 1.0s: echo "show table group_distinct_1m" | sudo socat unix:/run/haproxy/admin.sock - VM-16-16-ubuntu: Mon May 20 23:21:03 2024
# table: group_distinct_1m, type: string, size:1048576, used:6
0x56518c58ae10: key=deviced1 use=0 exp=130356 shard=0 gpc0=31 gpc0_rate(60000)=17 gpc1=5 gpc1_rate(60000)=2
0x56518c53a5f0: key=deviced1:fccd1 use=0 exp=93409 shard=0 gpc0=1 gpc0_rate(60000)=1 gpc1=0 gpc1_rate(60000)=0
0x56518c537d50: key=deviced1:fccd2 use=0 exp=97194 shard=0 gpc0=1 gpc0_rate(60000)=1 gpc1=0 gpc1_rate(60000)=0
0x56518c537eb0: key=deviced1:fccd3 use=0 exp=107137 shard=0 gpc0=9 gpc0_rate(60000)=6 gpc1=0 gpc1_rate(60000)=0
0x56518c5f4770: key=deviced1:fccd4 use=0 exp=117528 shard=0 gpc0=4 gpc0_rate(60000)=2 gpc1=0 gpc1_rate(60000)=0
0x56518c5f4ab0: key=deviced1:fccd5 use=0 exp=130356 shard=0 gpc0=16 gpc0_rate(60000)=10 gpc1=0 gpc1_rate(60000)=0
压测命令:
wrk -t12 -c400 -d30s --latency "http://150.158.144.155:88/group_distinct_1m?group=deviced1&v=fccd1"
haproxy config
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
tune.bufsize 2097152 # 2MB
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
frontend fe_api
bind :88
use_backend rate_10s if { path /rate_10s } #### \{ 与 path 之间要空格, uri 与 \} 之间也要保空格
use_backend rate_1m if { path /rate_1m }
use_backend rate_5m if { path /rate_5m }
use_backend rate_1h if { path /rate_1h }
use_backend rate_1d if { path /rate_1d }
use_backend rate_7d if { path /rate_7d }
use_backend rolling_sum10s if { path /rolling_sum10s }
use_backend group_distinct_1m if { path /group_distinct_1m }
use_backend group_distinct_5m if { path /group_distinct_5m }
use_backend group_distinct_1h if { path /group_distinct_1h }
use_backend group_distinct_1d if { path /group_distinct_1d }
use_backend group_distinct_7d if { path /group_distinct_7d }
default_backend default_be
backend default_be
http-request return status 200 content-type text/html file /etc/haproxy/sw_openapi/sw_func.html if { path / } # openapi entrypoint html
http-request return status 200 content-type text/css file /etc/haproxy/sw_openapi/swagger-ui/5.0.0/swagger-ui.min.css if { path_end swagger-ui.min.css }
http-request return status 200 content-type application/javascript file /etc/haproxy/sw_openapi/swagger-ui/5.0.0/swagger-ui-bundle.min.js if { path_end swagger-ui-bundle.min.js }
http-request return status 200 content-type application/json file /etc/haproxy/sw_openapi/sw_func.json if { path_end sw_func.json }
http-request return status 200 content-type application/json lf-string '{"ip": "%[src]","port": %cp,"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]", "timestamp":"%[date]"}' hdr Access-Control-Allow-Origin "*" if { path /inspect }
backend rate_10s
stick-table type binary len 16 size 1g expire 10s store gpc0,gpc0_rate(10s),gpc1,gpc1_rate(10s)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
# http-request set-var(txn.mykey) url_param(mykey)
http-request set-var(txn.v) url_param(mykey) unless { url_param(v) -m found } # distinct --> aggregate_key
http-request set-var(txn.v) url_param(v) if { url_param(v) -m found }
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.vttl) var(txn._v),table_expire
http-request set-var(txn.vttl) int(0) unless { var(txn.vttl) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.vidle) var(txn._v),table_idle
http-request set-var(txn.vidle) int(0) unless { var(txn.vidle) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
# stick-table type string size 1g expire 30s store gpc0,gpc0_rate(10s),gpc1,gpc1_rate(10s)
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._v) if is_post # track-sc0 会刷新 idle 和 ttl 的值.
http-request sc-inc-gpc0(0) if is_post # sc-inc-xxx 对键对应的值进行累加
http-request set-var(txn.counter) var(txn._v),table_gpc0
http-request set-var(txn.counter) int(0) unless { var(txn.counter) -m found }
http-request set-var(txn.rate) var(txn._v),table_gpc0_rate
http-request set-var(txn.rate) int(0) unless { var(txn.rate) -m found }
http-request return status 200 content-type application/json lf-string '{"counter":%[var(txn.rate)],"v":"%[var(txn.v)]","idle":%[var(txn.vidle)],"ttl":%[var(txn.vttl)],"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]","timestamp":"%[date]","ip": "%[src]","port": %cp}' hdr Access-Control-Allow-Origin "*"
backend rate_1m
stick-table type binary len 16 size 1g expire 1m store gpc0,gpc0_rate(1m),gpc1,gpc1_rate(1m)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
http-request set-var(txn.v) url_param(mykey) unless { url_param(v) -m found } # distinct --> aggregate_key
http-request set-var(txn.v) url_param(v) if { url_param(v) -m found }
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.vttl) var(txn._v),table_expire
http-request set-var(txn.vttl) int(0) unless { var(txn.vttl) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.vidle) var(txn._v),table_idle
http-request set-var(txn.vidle) int(0) unless { var(txn.vidle) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
# stick-table type string size 1g expire 30s store gpc0,gpc0_rate(10s),gpc1,gpc1_rate(10s)
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._v) if is_post # track-sc0 会刷新 idle 和 ttl 的值.
http-request sc-inc-gpc0(0) if is_post # sc-inc-xxx 对键对应的值进行累加
http-request set-var(txn.counter) var(txn._v),table_gpc0
http-request set-var(txn.counter) int(0) unless { var(txn.counter) -m found }
http-request set-var(txn.rate) var(txn._v),table_gpc0_rate
http-request set-var(txn.rate) int(0) unless { var(txn.rate) -m found }
http-request return status 200 content-type application/json lf-string '{"counter":%[var(txn.rate)],"v":"%[var(txn.v)]","idle":%[var(txn.vidle)],"ttl":%[var(txn.vttl)],"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]","timestamp":"%[date]","ip": "%[src]","port": %cp}' hdr Access-Control-Allow-Origin "*"
backend rate_5m
stick-table type binary len 16 size 1g expire 5m store gpc0,gpc0_rate(5m),gpc1,gpc1_rate(5m)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
http-request set-var(txn.v) url_param(mykey) unless { url_param(v) -m found } # distinct --> aggregate_key
http-request set-var(txn.v) url_param(v) if { url_param(v) -m found }
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.vttl) var(txn._v),table_expire
http-request set-var(txn.vttl) int(0) unless { var(txn.vttl) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.vidle) var(txn._v),table_idle
http-request set-var(txn.vidle) int(0) unless { var(txn.vidle) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
# stick-table type string size 1g expire 30s store gpc0,gpc0_rate(10s),gpc1,gpc1_rate(10s)
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._v) if is_post # track-sc0 会刷新 idle 和 ttl 的值.
http-request sc-inc-gpc0(0) if is_post # sc-inc-xxx 对键对应的值进行累加
http-request set-var(txn.counter) var(txn._v),table_gpc0
http-request set-var(txn.counter) int(0) unless { var(txn.counter) -m found }
http-request set-var(txn.rate) var(txn._v),table_gpc0_rate
http-request set-var(txn.rate) int(0) unless { var(txn.rate) -m found }
http-request return status 200 content-type application/json lf-string '{"counter":%[var(txn.rate)],"v":"%[var(txn.v)]","idle":%[var(txn.vidle)],"ttl":%[var(txn.vttl)],"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]","timestamp":"%[date]","ip": "%[src]","port": %cp}' hdr Access-Control-Allow-Origin "*"
backend rate_1h
stick-table type binary len 16 size 1g expire 1h store gpc0,gpc0_rate(1h),gpc1,gpc1_rate(1h)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
http-request set-var(txn.v) url_param(mykey) unless { url_param(v) -m found } # distinct --> aggregate_key
http-request set-var(txn.v) url_param(v) if { url_param(v) -m found }
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.vttl) var(txn._v),table_expire
http-request set-var(txn.vttl) int(0) unless { var(txn.vttl) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.vidle) var(txn._v),table_idle
http-request set-var(txn.vidle) int(0) unless { var(txn.vidle) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
# stick-table type string size 1g expire 30s store gpc0,gpc0_rate(10s),gpc1,gpc1_rate(10s)
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._v) if is_post # track-sc0 会刷新 idle 和 ttl 的值.
http-request sc-inc-gpc0(0) if is_post # sc-inc-xxx 对键对应的值进行累加
http-request set-var(txn.counter) var(txn._v),table_gpc0
http-request set-var(txn.counter) int(0) unless { var(txn.counter) -m found }
http-request set-var(txn.rate) var(txn._v),table_gpc0_rate
http-request set-var(txn.rate) int(0) unless { var(txn.rate) -m found }
http-request return status 200 content-type application/json lf-string '{"counter":%[var(txn.rate)],"v":"%[var(txn.v)]","idle":%[var(txn.vidle)],"ttl":%[var(txn.vttl)],"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]","timestamp":"%[date]","ip": "%[src]","port": %cp}' hdr Access-Control-Allow-Origin "*"
backend rate_1d
stick-table type binary len 16 size 1g expire 1d store gpc0,gpc0_rate(1d),gpc1,gpc1_rate(1d)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
http-request set-var(txn.v) url_param(mykey) unless { url_param(v) -m found } # distinct --> aggregate_key
http-request set-var(txn.v) url_param(v) if { url_param(v) -m found }
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.vttl) var(txn._v),table_expire
http-request set-var(txn.vttl) int(0) unless { var(txn.vttl) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.vidle) var(txn._v),table_idle
http-request set-var(txn.vidle) int(0) unless { var(txn.vidle) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
# stick-table type string size 1g expire 30s store gpc0,gpc0_rate(10s),gpc1,gpc1_rate(10s)
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._v) if is_post # track-sc0 会刷新 idle 和 ttl 的值.
http-request sc-inc-gpc0(0) if is_post # sc-inc-xxx 对键对应的值进行累加
http-request set-var(txn.counter) var(txn._v),table_gpc0
http-request set-var(txn.counter) int(0) unless { var(txn.counter) -m found }
http-request set-var(txn.rate) var(txn._v),table_gpc0_rate
http-request set-var(txn.rate) int(0) unless { var(txn.rate) -m found }
http-request return status 200 content-type application/json lf-string '{"counter":%[var(txn.rate)],"v":"%[var(txn.v)]","idle":%[var(txn.vidle)],"ttl":%[var(txn.vttl)],"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]","timestamp":"%[date]","ip": "%[src]","port": %cp}' hdr Access-Control-Allow-Origin "*"
backend rate_7d
stick-table type binary len 16 size 1g expire 7d store gpc0,gpc0_rate(7d),gpc1,gpc1_rate(7d)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
http-request set-var(txn.v) url_param(mykey) unless { url_param(v) -m found } # distinct --> aggregate_key
http-request set-var(txn.v) url_param(v) if { url_param(v) -m found }
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.vttl) var(txn._v),table_expire
http-request set-var(txn.vttl) int(0) unless { var(txn.vttl) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.vidle) var(txn._v),table_idle
http-request set-var(txn.vidle) int(0) unless { var(txn.vidle) -m found } ## 如果前面没有设置 txn.vttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
# stick-table type string size 1g expire 30s store gpc0,gpc0_rate(10s),gpc1,gpc1_rate(10s)
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._v) if is_post # track-sc0 会刷新 idle 和 ttl 的值.
http-request sc-inc-gpc0(0) if is_post # sc-inc-xxx 对键对应的值进行累加
http-request set-var(txn.counter) var(txn._v),table_gpc0
http-request set-var(txn.counter) int(0) unless { var(txn.counter) -m found }
http-request set-var(txn.rate) var(txn._v),table_gpc0_rate
http-request set-var(txn.rate) int(0) unless { var(txn.rate) -m found }
http-request return status 200 content-type application/json lf-string '{"counter":%[var(txn.rate)],"v":"%[var(txn.v)]","idle":%[var(txn.vidle)],"ttl":%[var(txn.vttl)],"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]","timestamp":"%[date]","ip": "%[src]","port": %cp}' hdr Access-Control-Allow-Origin "*"
backend rolling_sum10s
stick-table type binary len 16 size 1g expire 20s store gpc(1),gpc_rate(1,20s) # ,gpc1,gpc1_rate(10s)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
http-request set-var(txn.key) url_param(key)
http-request set-var(txn.num) url_param(num)
http-request set-var(txn._key) var(txn.key),digest(md5)
http-request set-var(txn.keyttl) var(txn._key),table_expire
http-request set-var(txn.keyttl) int(0) unless { var(txn.keyttl) -m found } ## 如果前面没有设置 txn.keyttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.keyidle) var(txn._key),table_idle
http-request set-var(txn.keyidle) int(0) unless { var(txn.keyidle) -m found } ## 如果前面没有设置 txn.keyttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
# stick-table type string size 1g expire 30s store gpc0,gpc0_rate(10s),gpc1,gpc1_rate(10s)
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._key) if is_post # track-sc0 会刷新 idle 和 ttl 的值.
#http-request sc-inc-gpc0(0) if is_post # sc-inc-xxx 对键对应的值进行累加
http-request sc-add-gpc(0,0) var(txn.num) if is_post # var(txn.num) # int(10)
http-request set-var(txn.counter) var(txn._key),table_gpc(0,) # table_gpc0
http-request set-var(txn.counter) int(0) unless { var(txn.counter) -m found }
http-request set-var(txn.rate) var(txn._key),table_gpc_rate(0,)
http-request set-var(txn.rate) int(0) unless { var(txn.rate) -m found }
http-request return status 200 content-type application/json lf-string '{"counter":%[var(txn.rate)],"key":"%[var(txn.key)]","ttl":%[var(txn.keyttl)],"idle":%[var(txn.keyidle)],"ip": "%[src]","port": %cp,"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]", "timestamp":"%[date]"}' hdr Access-Control-Allow-Origin "*"
backend group_distinct_1m
stick-table type binary len 16 size 1g expire 1m store gpc0,gpc0_rate(1m),gpc1,gpc1_rate(1m)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
# 定义变量
http-request set-var(txn.group) url_param(group) # group --> group
http-request set-var(txn.v) url_param(distinct) unless { url_param(v) -m found } # distinct --> aggregate_key
http-request set-var(txn.v) url_param(v) if { url_param(v) -m found } # distinct --> aggregate_key
# http-request set-var-fmt(txn.combine_key) "%[var(txn.group)]%[var(txn.v)]"
http-request set-var(txn.combine_key) var(txn.group),concat(':',txn.v,) # 避免 v 为空值或者不传入分组待观测值时导致 combine_key 和 key 一样,进而重复计数.
# str(1),digest(md5),hex # str(),concat(<ip=,sess.ip,>),concat(<dn=,sess.dn,>)
http-request set-var(txn._combine_key) var(txn.combine_key),digest(md5)
http-request set-var(txn._group) var(txn.group),digest(md5)
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.gttl) var(txn._group),table_expire
http-request set-var(txn.gttl) int(0) unless { var(txn.gttl) -m found } ## 如果前面没有设置 txn.gttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.gidle) var(txn._group),table_idle
http-request set-var(txn.gidle) int(0) unless { var(txn.gidle) -m found } ## 如果前面没有设置 txn.gttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.vidle) var(txn._combine_key),table_idle
http-request set-var(txn.vidle) int(0) unless { var(txn.vidle) -m found }
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._group) if is_post # track-scx 会刷新 idle 和 ttl 的值.
http-request track-sc1 var(txn._combine_key) if is_post # track-scx 会刷新 idle 和 ttl 的值.
# 先取出 table 中 txn.combine_key 的速率, 如果最近窗口速率等于0说明txn.combine_key是第一次出现. 然后再去完成 txn.combine_key 值的累计.
http-request sc-inc-gpc1(0) if is_post { var(txn._combine_key),table_gpc0_rate eq 0 } # 对 group:aggregate_key 这个复合键如果是第一次出现, 那么在gpc1中对group的进行自增(UV的概念).
http-request sc-inc-gpc0(0) if is_post ## 在 gpc0 中对 group 的进行累计(PV的概念)
http-request sc-inc-gpc0(1) if is_post ## 对 track-sc1 中的 group:aggregate_key 在 gpc0 中进行计数(PV)
http-request set-var(txn.gpv) var(txn._group),table_gpc0_rate
http-request set-var(txn.uv) var(txn._group),table_gpc1_rate
http-request set-var(txn.pv) var(txn._combine_key),table_gpc0_rate
http-request return status 200 content-type application/json lf-string '{"gpv":%[var(txn.gpv)],"pv":%[var(txn.pv)],"uv":%[var(txn.uv)],"gidle":%[var(txn.gidle)],"vidle":%[var(txn.vidle)],"group":"%[var(txn.group)]","v":"%[var(txn.v)]","ttl":%[var(txn.gttl)],"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]","timestamp":"%[date]","ip": "%[src]","port": %cp}' hdr Access-Control-Allow-Origin "*"
backend group_distinct_5m
stick-table type binary len 16 size 1g expire 5m store gpc0,gpc0_rate(5m),gpc1,gpc1_rate(5m)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
# 定义变量
http-request set-var(txn.group) url_param(group) # group --> group
http-request set-var(txn.v) url_param(distinct) unless { url_param(v) -m found } # distinct --> aggregate_key
http-request set-var(txn.v) url_param(v) if { url_param(v) -m found } # distinct --> aggregate_key
# http-request set-var-fmt(txn.combine_key) "%[var(txn.group)]%[var(txn.v)]"
http-request set-var(txn.combine_key) var(txn.group),concat(':',txn.v,) # 避免 v 为空值或者不传入分组待观测值时导致 combine_key 和 key 一样,进而重复计数.
# str(1),digest(md5),hex # str(),concat(<ip=,sess.ip,>),concat(<dn=,sess.dn,>)
http-request set-var(txn._combine_key) var(txn.combine_key),digest(md5)
http-request set-var(txn._group) var(txn.group),digest(md5)
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.gttl) var(txn._group),table_expire
http-request set-var(txn.gttl) int(0) unless { var(txn.gttl) -m found } ## 如果前面没有设置 txn.gttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.gidle) var(txn._group),table_idle
http-request set-var(txn.gidle) int(0) unless { var(txn.gidle) -m found } ## 如果前面没有设置 txn.gttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.vidle) var(txn._combine_key),table_idle
http-request set-var(txn.vidle) int(0) unless { var(txn.vidle) -m found }
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._group) if is_post # track-scx 会刷新 idle 和 ttl 的值.
http-request track-sc1 var(txn._combine_key) if is_post # track-scx 会刷新 idle 和 ttl 的值.
# 先取出 table 中 txn.combine_key 的速率, 如果最近窗口速率等于0说明txn.combine_key是第一次出现. 然后再去完成 txn.combine_key 值的累计.
http-request sc-inc-gpc1(0) if is_post { var(txn._combine_key),table_gpc0_rate eq 0 } # 对 group:aggregate_key 这个复合键如果是第一次出现, 那么在gpc1中对group的进行自增(UV的概念).
http-request sc-inc-gpc0(0) if is_post ## 在 gpc0 中对 group 的进行累计(PV的概念)
http-request sc-inc-gpc0(1) if is_post ## 对 track-sc1 中的 group:aggregate_key 在 gpc0 中进行计数(PV)
http-request set-var(txn.gpv) var(txn._group),table_gpc0_rate
http-request set-var(txn.uv) var(txn._group),table_gpc1_rate
http-request set-var(txn.pv) var(txn._combine_key),table_gpc0_rate
http-request return status 200 content-type application/json lf-string '{"gpv":%[var(txn.gpv)],"pv":%[var(txn.pv)],"uv":%[var(txn.uv)],"gidle":%[var(txn.gidle)],"vidle":%[var(txn.vidle)],"group":"%[var(txn.group)]","v":"%[var(txn.v)]","ttl":%[var(txn.gttl)],"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]","timestamp":"%[date]","ip": "%[src]","port": %cp}' hdr Access-Control-Allow-Origin "*"
backend group_distinct_1h
stick-table type binary len 16 size 1g expire 1h store gpc0,gpc0_rate(1h),gpc1,gpc1_rate(1h)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
# 定义变量
http-request set-var(txn.group) url_param(group) # group --> group
# http-request set-var(txn.gttl) str()
http-request set-var(txn.v) url_param(distinct) unless { url_param(v) -m found } # distinct --> aggregate_key
http-request set-var(txn.v) url_param(v) if { url_param(v) -m found }
# http-request set-var-fmt(txn.combine_key) "%[var(txn.group)]%[var(txn.v)]"
http-request set-var(txn.combine_key) var(txn.group),concat(':',txn.v,) # 避免 v 为空值或者不传入分组待观测值时导致 combine_key 和 key 一样,进而重复计数. # 避免 v 为空值或者不传入分组待观测值时导致 combine_key 和 key 一样,进而重复计数.
# str(1),digest(md5),hex # str(),concat(<ip=,sess.ip,>),concat(<dn=,sess.dn,>)
http-request set-var(txn._combine_key) var(txn.combine_key),digest(md5)
http-request set-var(txn._group) var(txn.group),digest(md5)
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.gttl) var(txn._group),table_expire
http-request set-var(txn.gttl) int(0) unless { var(txn.gttl) -m found } ## 如果前面没有设置 txn.gttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.gidle) var(txn._group),table_idle
http-request set-var(txn.gidle) int(0) unless { var(txn.gidle) -m found } ## 如果前面没有设置 txn.gttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.vidle) var(txn._combine_key),table_idle
http-request set-var(txn.vidle) int(0) unless { var(txn.vidle) -m found }
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._group) if is_post # track-scx 会刷新 idle 和 ttl 的值.
http-request track-sc1 var(txn._combine_key) if is_post # track-scx 会刷新 idle 和 ttl 的值.
# 先取出 table 中 txn.combine_key 的速率, 如果最近窗口速率等于0说明txn.combine_key是第一次出现. 然后再去完成 txn.combine_key 值的累计.
http-request sc-inc-gpc1(0) if is_post { var(txn._combine_key),table_gpc0_rate eq 0 } # 对 group:aggregate_key 这个复合键如果是第一次出现, 那么在gpc1中对group的进行自增(UV的概念).
http-request sc-inc-gpc0(0) if is_post ## 在 gpc0 中对 group 的进行累计(PV的概念)
http-request sc-inc-gpc0(1) if is_post ## 对 track-sc1 中的 group:aggregate_key 在 gpc0 中进行计数(PV)
http-request set-var(txn.gpv) var(txn._group),table_gpc0_rate
http-request set-var(txn.uv) var(txn._group),table_gpc1_rate
http-request set-var(txn.pv) var(txn._combine_key),table_gpc0_rate
http-request return status 200 content-type application/json lf-string '{"gpv":%[var(txn.gpv)],"pv":%[var(txn.pv)],"uv":%[var(txn.uv)],"gidle":%[var(txn.gidle)],"vidle":%[var(txn.vidle)],"group":"%[var(txn.group)]","v":"%[var(txn.v)]","ttl":%[var(txn.gttl)],"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]","timestamp":"%[date]","ip": "%[src]","port": %cp}' hdr Access-Control-Allow-Origin "*"
backend group_distinct_1d
stick-table type binary len 16 size 1g expire 1d store gpc0,gpc0_rate(1d),gpc1,gpc1_rate(1d)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
# 定义变量
http-request set-var(txn.group) url_param(group) # group --> group
http-request set-var(txn.v) url_param(distinct) unless { url_param(v) -m found } # distinct --> aggregate_key
http-request set-var(txn.v) url_param(v) if { url_param(v) -m found } # distinct --> aggregate_key
# http-request set-var-fmt(txn.combine_key) "%[var(txn.group)]%[var(txn.v)]"
http-request set-var(txn.combine_key) var(txn.group),concat(':',txn.v,) # 避免 v 为空值或者不传入分组待观测值时导致 combine_key 和 key 一样,进而重复计数.
# str(1),digest(md5),hex # str(),concat(<ip=,sess.ip,>),concat(<dn=,sess.dn,>)
http-request set-var(txn._combine_key) var(txn.combine_key),digest(md5)
http-request set-var(txn._group) var(txn.group),digest(md5)
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.gttl) var(txn._group),table_expire
http-request set-var(txn.gttl) int(0) unless { var(txn.gttl) -m found } ## 如果前面没有设置 txn.gttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.gidle) var(txn._group),table_idle
http-request set-var(txn.gidle) int(0) unless { var(txn.gidle) -m found } ## 如果前面没有设置 txn.gttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.vidle) var(txn._combine_key),table_idle
http-request set-var(txn.vidle) int(0) unless { var(txn.vidle) -m found }
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._group) if is_post # track-scx 会刷新 idle 和 ttl 的值.
http-request track-sc1 var(txn._combine_key) if is_post # track-scx 会刷新 idle 和 ttl 的值.
# 先取出 table 中 txn.combine_key 的速率, 如果最近窗口速率等于0说明txn.combine_key是第一次出现. 然后再去完成 txn.combine_key 值的累计.
http-request sc-inc-gpc1(0) if is_post { var(txn._combine_key),table_gpc0_rate eq 0 } # 对 group:aggregate_key 这个复合键如果是第一次出现, 那么在gpc1中对group的进行自增(UV的概念).
http-request sc-inc-gpc0(0) if is_post ## 在 gpc0 中对 group 的进行累计(PV的概念)
http-request sc-inc-gpc0(1) if is_post ## 对 track-sc1 中的 group:aggregate_key 在 gpc0 中进行计数(PV)
http-request set-var(txn.gpv) var(txn._group),table_gpc0_rate
http-request set-var(txn.uv) var(txn._group),table_gpc1_rate
http-request set-var(txn.pv) var(txn._combine_key),table_gpc0_rate
http-request return status 200 content-type application/json lf-string '{"gpv":%[var(txn.gpv)],"pv":%[var(txn.pv)],"uv":%[var(txn.uv)],"gidle":%[var(txn.gidle)],"vidle":%[var(txn.vidle)],"group":"%[var(txn.group)]","v":"%[var(txn.v)]","ttl":%[var(txn.gttl)],"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]","timestamp":"%[date]","ip": "%[src]","port": %cp}' hdr Access-Control-Allow-Origin "*"
backend group_distinct_7d
stick-table type binary len 16 size 1g expire 7d store gpc0,gpc0_rate(7d),gpc1,gpc1_rate(7d)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
# 定义变量
http-request set-var(txn.group) url_param(group) # group --> group
http-request set-var(txn.v) url_param(distinct) unless { url_param(v) -m found } # distinct --> aggregate_key
http-request set-var(txn.v) url_param(v) if { url_param(v) -m found } # distinct --> aggregate_key
# http-request set-var-fmt(txn.combine_key) "%[var(txn.group)]%[var(txn.v)]"
http-request set-var(txn.combine_key) var(txn.group),concat(':',txn.v,) # 避免 v 为空值或者不传入分组待观测值时导致 combine_key 和 key 一样,进而重复计数.
# str(1),digest(md5),hex # str(),concat(<ip=,sess.ip,>),concat(<dn=,sess.dn,>)
http-request set-var(txn._combine_key) var(txn.combine_key),digest(md5)
http-request set-var(txn._group) var(txn.group),digest(md5)
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.gttl) var(txn._group),table_expire
http-request set-var(txn.gttl) int(0) unless { var(txn.gttl) -m found } ## 如果前面没有设置 txn.gttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.gidle) var(txn._group),table_idle
http-request set-var(txn.gidle) int(0) unless { var(txn.gidle) -m found } ## 如果前面没有设置 txn.gttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.vidle) var(txn._combine_key),table_idle
http-request set-var(txn.vidle) int(0) unless { var(txn.vidle) -m found }
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._group) if is_post # track-scx 会刷新 idle 和 ttl 的值.
http-request track-sc1 var(txn._combine_key) if is_post # track-scx 会刷新 idle 和 ttl 的值.
# 先取出 table 中 txn.combine_key 的速率, 如果最近窗口速率等于0说明txn.combine_key是第一次出现. 然后再去完成 txn.combine_key 值的累计.
http-request sc-inc-gpc1(0) if is_post { var(txn._combine_key),table_gpc0_rate eq 0 } # 对 group:aggregate_key 这个复合键如果是第一次出现, 那么在gpc1中对group的进行自增(UV的概念).
http-request sc-inc-gpc0(0) if is_post ## 在 gpc0 中对 group 的进行累计(PV的概念)
http-request sc-inc-gpc0(1) if is_post ## 对 track-sc1 中的 group:aggregate_key 在 gpc0 中进行计数(PV)
http-request set-var(txn.gpv) var(txn._group),table_gpc0_rate
http-request set-var(txn.uv) var(txn._group),table_gpc1_rate
http-request set-var(txn.pv) var(txn._combine_key),table_gpc0_rate
http-request return status 200 content-type application/json lf-string '{"gpv":%[var(txn.gpv)],"pv":%[var(txn.pv)],"uv":%[var(txn.uv)],"gidle":%[var(txn.gidle)],"vidle":%[var(txn.vidle)],"group":"%[var(txn.group)]","v":"%[var(txn.v)]","ttl":%[var(txn.gttl)],"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]","timestamp":"%[date]","ip": "%[src]","port": %cp}' hdr Access-Control-Allow-Origin "*"
roadmap
现在是使用 haproxy 来进行计数, 目前用的都是 inc, 如果以后 sum 的场景, 这个就需要 add 操作.
sc-add-gpc
sc-inc-gpc
sc-add-gpc
backend rolling_sum10s
stick-table type binary len 16 size 1g expire 20s store gpc(1),gpc_rate(1,20s) # ,gpc1,gpc1_rate(10s)
acl is_post method POST
# 在 sc-inc-gpc0 之前获取 ttl(expire) 和 idle 信息.
http-request set-var(txn.v) url_param(v)
http-request set-var(txn.num) url_param(num)
http-request set-var(txn._v) var(txn.v),digest(md5)
http-request set-var(txn.keyttl) var(txn._v),table_expire
http-request set-var(txn.keyttl) int(0) unless { var(txn.keyttl) -m found } ## 如果前面没有设置 txn.keyttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
http-request set-var(txn.keyidle) var(txn._v),table_idle
http-request set-var(txn.keyidle) int(0) unless { var(txn.keyidle) -m found } ## 如果前面没有设置 txn.keyttl table 中没有这个记录, 是第一次出现, 这里进行初始化.
# stick-table type string size 1m expire 30s store gpc0,gpc0_rate(10s),gpc1,gpc1_rate(10s)
# 在 sc-inc-gpc0 之后, table key 的 ttl 和 idle 时间会重置.
# track-scX 和 sc-inc-gpc0(X) 里面的 X 是 sc0, sc1, sc2 中的一个.
http-request track-sc0 var(txn._v) #if is_post # track-sc0 会刷新 idle 和 ttl 的值.
#http-request sc-inc-gpc0(0) if is_post # sc-inc-xxx 对键对应的值进行累加
http-request sc-add-gpc(0,0) int(10) # if is_post # var(txn.num)
http-request set-var(txn.counter) var(txn._v),table_gpc(0,) # table_gpc0
http-request set-var(txn.counter) int(0) unless { var(txn.counter) -m found }
http-request set-var(txn.rate) var(txn._v),table_gpc_rate(0,)
http-request set-var(txn.rate) int(0) unless { var(txn.rate) -m found }
http-request return status 200 content-type application/json lf-string '{"counter":%[var(txn.rate)],"v":"%[var(txn.v)]","ttl":%[var(txn.keyttl)],"idle":%[var(txn.keyidle)],"ip": "%[src]","port": %cp,"date":"%[date,utime(%Y-%m-%dT%H:%M:%S%z)]", "timestamp":"%[date]"}' hdr Access-Control-Allow-Origin "*"
curl -XPOST "http://10.84.71.214:88/rolling_sum10s?v=abcdef&num=10"