VOMD通常在vcl_init中配置,sqlite3和memcacheed也是这样。对于sqlite3,我们设置数据库路径和用在多列结果中的分隔符。memcached VMOD可以有各种各样 libmemcached 支持的配置选项。
sub vcl_init {
sqlite3.open("/tmp/rules.db3", "|;");
memcached.servers("--SERVER=localhost --BINARY-PROTOCOL");
}
在vcl_recv中,传入的HTTP请求被接受。我们首先提取没有查询参数和潜在危险字符的请求路径。这非常重要,因为这个路径是稍后SQL请求的一部分。接下来的正则表达式将从一行的开始直到字符(?&;”')或空格结束匹配req.url。
sub vcl_recv {
set req.http.path = regsub(req.url, {"^([^?&;"' ]+).*"}, "\1");
在正则表达式中使用{”“}可以支持正则表达式规则中“字符的处理。我们刚刚提取的路径仅在数据库中查找规则时使用。响应(如果有的话)存储在req.hhtp.rule中。
set req.http.rule = sqlite3.exec("SELECT rule FROM t WHERE path='" + req.http.path + "' LIMIT 1");
如果我们得到一个响应,它将会是RnT格式的,这里的R是T秒时间段内允许的请求量。由于这是一个字符串,我们需要应用额外的正则表达式来分割。
set req.http.requests = regsub(req.http.rule, "^([0-9]+)r.*$", "\1");
set req.http.period = regsub(req.http.rule, "^[0-9]+r([0-9]+)$", "\1");
只有当我们从前面的正则表达式过滤器中获得正确的值时才限制请求。
if (req.http.requests != "" && req.http.period != "") {
给这个client.ip增加或创建一个独一无二的Memcached计数器。并将path值设置为1。我们将失效时间指定为与数据库中限制规则设置的时间相同。在这种方式中,限制规则可以灵活地设置时间。返回值就是计数器的新值,与这个client.ip在当前路径和当前时间段内的请求数量相符。
set req.http.counter = memcached.incr_set(
req.http.path + "-" + client.ip, 1, 1, std.integer(req.http.period, 0));
检查计数器是否高于数据库中设置的限制。如果是,放弃当前的请求并返回一个429响应码。
if (std.integer(req.http.counter, 0) > std.integer(req.http.requests, 0)) {
return (synth(429, "Too many requests"));
}
}
}
在 vxl_deliver 中我们设置了显示限流限制的响应 headers 和有助于用户的每一个请求状态。
sub vcl_deliver {
if (req.http.requests && req.http.counter && req.http.period) {
set resp.http.X-RateLimit-Limit = req.http.requests;
set resp.http.X-RateLimit-Counter = req.http.counter;
set resp.http.X-RateLimit-Period = req.http.period;
}
}
在 vcl_synth 中,错误将获得一个同样的 headers 设置。
sub vcl_synth {
if (req.http.requests && req.http.counter && req.http.period) {
set resp.http.X-RateLimit-Limit = req.http.requests;
set resp.http.X-RateLimit-Counter = req.http.counter;
set resp.http.X-RateLimit-Period = req.http.period;
}
}