NGINX - OpenResty - Как отменить прокси-вызов для 2 разных серверов на основе строки?

Я пытаюсь настроить NGINX/OpenResty для прокси-вызовов SOAP на 2 разных сервера на основе строки, присутствующей в запросе SOAP.

Что я могу сделать: я могу прокси-запросы на 2 разных сервера в зависимости от пути, который вызывает клиент SOAP:

location /pathA {
    proxy_pass http://www.ServerA.com/PathA/;
}
location /pathB {
    proxy_pass http://www.ServerB.com/PathB/;
}

Что я не могу сделать:

Я не могу разделить трафик в зависимости от содержания запроса. Основная причина, по которой я верю, заключается в том, что я не могу правильно собрать скрипт LUA для извлечения информации и последующего использования для прокси-запроса.

location / {
    conten_by_lua '
        ngx.req.read_body()
        local match = ngx.re.match(ngx.var.request_body,"STRING TO FIND")
        if match then
            proxy_pass http://www.ServerA.com/PathA/;
        else
            proxy_pass http://www.ServerB.com/PathB/;

как я могу этого достичь?

Я установил OpenResty и LUA работает нормально.

Я думаю, что я где-то читал, что если запрос не HTTP POST ngx.req.read_body() не будет работать. Это верно?

Спасибо за помощь.

2 ответа

Вы почти сделали это сами, единственное, что я сделал бы по-другому, это использовать rewrite_by_lua вместо content_by_lua:

location / {
    set $proxy "";
    rewrite_by_lua '
        ngx.req.read_body()
        local match = ngx.re.match(ngx.var.request_body, "STRING TO FIND")
        if match then
            ngx.var.proxy = "www.ServerA.com"
        else
            ngx.var.proxy = "www.ServerB.com"
        end
    ';
    proxy_pass http://$proxy$uri;
}

Если запрос не является HTTP POST или имеет пустое тело, ngx.req.read_body() вернусь nil, так что лучше добавить дополнительную проверку:

location / {
    set $proxy "";
    rewrite_by_lua '
        ngx.req.read_body()
        local body = ngx.var.request_body
        if (body) then
            local match = ngx.re.match(body, "STRING TO FIND")
            if match then
                ngx.var.proxy = "www.ServerA.com"
            else
                ngx.var.proxy = "www.ServerB.com"
            end
        else
            ngx.status = ngx.HTTP_NOT_FOUND
            ngx.exit(ngx.status)
        end
    ';
    proxy_pass http://$proxy$uri;
}

Вы также можете ограничить допустимые методы:

location / {
    limit_except POST { deny all; }
    ...
}

Еще кое-что. В этой конфигурации, если вы укажете свои бэкэнды с доменными именами вместо IP-адресов, вам понадобится resolver Директива в вашем конфиге. Вы можете использовать свой локальный сервер имен, если он у вас есть, или использовать что-то внешнее, например, общедоступный DNS Google (8.8.8.8) или DNS, предоставленный вам вашим провайдером.

Вот фрагменты кода, которые я использовал в openresty, чтобы различать веб-перехватчики dev и prod в соответствии с опубликованным телом json:

      server {
    listen 8080;
    resolver 114.114.114.114 8.8.8.8 valid=30s;

    location /webhook {
        limit_except POST { deny all; }

        set $proxy "";

        rewrite_by_lua_block {
            local devWebhook = "myservice.dev"   -- todo
            local prodWebhook = "mysevice.prod" -- todo

            function getFile(file_name)
                local f = assert(io.open(file_name, 'r'))
                local string = f:read("*all")
                f:close()
                return string
            end

            ngx.req.read_body()
            local data = ngx.req.get_body_data()
            if nil == data then
                local file_name = ngx.req.get_body_file()
                if file_name then
                    data = getFile(file_name)
                end
            end

            local json = require("cjson.safe")
            local t    = json.decode(data)
            if not (type(t) == "table" and t["msg"]) then
                ngx.exit(400)
            end               

            local msg = ngx.unescape_uri(t["msg"])
            if not (type(msg) == "string" and #msg > 0) then
                ngx.exit(400)
            end

            if string.find(msg, '"title":".*test product.*"') then
                ngx.var.proxy = devWebhook
            else
                ngx.var.proxy = prodWebhook
            end
        }

        proxy_pass http://$proxy$uri;
    }
}
Другие вопросы по тегам