Devops's Blog

HAproxy+lua_1 Тромазим трафик

  • Для запросов на /search:
    • Считаем количество запросов от устройства (по sc_get_gpc0).
    • Первый запрос — без задержки.
    • Каждый следующий запрос задерживается на delay = (номер запроса - 1) * 4 секунд, но не более 32 секунд.
    • Максимальная задержка — 32 секунды, начиная с 9 запроса.
    • Если заголовок xbots2 отсутствует — проверка не производится.
    • В лог пишется информация о каждом задержанном запросе.

    Таблица задержек:

    № запроса Задержка (секунд)
    10
    24
    38
    412
    516
    620
    724
    828
    932
    10 и далее32
  • Для запросов на conver:
    • Считает количество запросов от устройства (по sc_get_gpc1).
    • Первый запрос — без задержки.
    • Все последующие — задержка 1 секунда на каждый запрос.

    Таблица задержек:

    № запроса Задержка (секунд)
    10
    21
    31
    41
    1

lua-load        device_ratelimit.lua

acl mobile_search path -i /search
acl mobile_conver path -i /conver

http-request set-header xbots2 %[req.fhdr(X-Device)] if { hdr_cnt(X-Device) eq 1 }
http-request track-sc1 hdr(xbots2) table bruteforce_ext_1h_2 if { hdr_cnt(X-Device) eq 1 }
http-request sc-inc-gpc0(1) if { hdr_cnt(X-Device) eq 1 } mobile_search
http-request sc-inc-gpc1(1) if { hdr_cnt(X-Device) eq 1 } mobile_conver
http-request lua.device_rate_limit if { hdr_cnt(X-Device) eq 1 } mobile_search
http-request lua.device_rate_limit if { hdr_cnt(X-Device) eq 1 } mobile_conver
http-request del-header xbots2
 

local max_delay = 32
local step = 4
local conver_delay = 1

core.register_action("device_rate_limit", {"http-req"}, function(txn)
    local key = txn.sf:req_hdr("xbots2")
    if not key or key == '' then return end

    local path = txn.sf:path()
    local is_search = string.find(path, "/search", 1, true) ~= nil
    local is_conver = string.find(path, "/conver", 1, true) ~= nil

    if is_search then
        local count = tonumber(txn.sf:sc_get_gpc0(1)) or 0
        local current_request = count + 1

        if current_request == 1 then
            return
        end

        local delay = math.min((current_request - 1) * step, max_delay)
        if delay > 0 then
            txn:Info("device_rate_limit(search): x-device=" .. key .. " count=" .. count .. " delay=" .. delay)
            core.msleep(delay * 1000)
        end

        return
    end

    if is_conver then
        local count = tonumber(txn.sf:sc_get_gpc1(1)) or 0
        local current_request = count + 1

        if current_request == 1 then
            return
        end

        txn:Info("device_rate_limit(conver): x-device=" .. key .. " conv-count=" .. count .. " delay=" .. conver_delay)
        core.msleep(conver_delay * 1000)
        return
    end
end)