Проблема разделения исходного запроса и зеркального запроса в nginx - PullRequest
1 голос
/ 27 июня 2019

У меня есть 2 среды (envA, envB).envA необходимо отразить свои запросы к envB, а также сделать 2 других вызова envB, содержащих информацию из ответа в envA.envA не заинтересован в ответе envB, это, по сути, ситуация «забей и забудь».Цель состоит в том, чтобы убедиться, что работа и производительность envA никоим образом не зависят от вызовов, сделанных в envB.Мы решили использовать nginx в качестве нашего прокси и заставить его выполнять зеркалирование.Мы также написали скрипт lua для обработки логики, которую я описал выше.

Проблема в том, что, хотя ответ от служб envA возвращается быстро, nginx задерживает возврат ответа envA вызывающей стороне, пока не будет сделано 3 других вызова envB.Я хочу как-то избавиться от этой блокировки.

В нашей команде нет никого, кто имел опыт работы с lua или nginx, поэтому я уверен, что то, что у нас есть, не лучший / правильный способ сделатьэто ... но то, что мы делали до сих пор, это настроить соединение и считать тайм-ауты, чтобы убедиться, что мы уменьшаем любую блокировку до минимального количества времени.Но это просто не приводит нас туда, где мы хотим быть.

После некоторых исследований я нашел https://github.com/openresty/lua-nginx-module#ngxtimerat, который;как я понял;было бы то же самое, что и создание ScheduledThreadPoolExecutor в java и просто наложение на него задания и отделение его от потока исходного запроса, таким образом устраняя блокировку.Однако я не знаю достаточно о том, как меняется область действия, чтобы быть уверенным, что я не напортачу что-либо в отношении данных / переменных, и я также не уверен, какие библиотеки использовать для вызовов envB, так как мы использовали ngxДо сих пор .location.capture, который согласно документации по ссылке выше, не подходит при использовании ngx.timer.at.Поэтому я был бы признателен за понимание того, как правильно использовать ngx.timer.at или альтернативные подходы для достижения этой цели.

Это код lua, который мы используем.Я запутал это много, но кости того, что у нас есть, есть, и основная часть - это раздел content_by_lua_block

http {
    upstream envA {
        server {{getenv "ENVA_URL"}};
    }
    upstream envB {
        server {{getenv "ENVB_URL"}};
    }

    server {
        underscores_in_headers on;
        aio threads=one;
        listen       443 ssl;

        ssl_certificate     {{getenv "CERT"}};
        ssl_certificate_key {{getenv "KEY"}};

        location /{{getenv "ENDPOINT"}}/ {
            content_by_lua_block {
                ngx.req.set_header("x-original-uri", ngx.var.uri)
                ngx.req.set_header("x-request-method", ngx.var.echo_request_method)
                resp = ""
                ngx.req.read_body()

                if (ngx.var.echo_request_method == 'POST') then
                    local request = ngx.req.get_body_data()
                    resp = ngx.location.capture("/envA" .. ngx.var.request_uri, { method = ngx.HTTP_POST })
                    ngx.location.capture("/mirror/envB" .. ngx.var.uri, { method = ngx.HTTP_POST })
                    ngx.location.capture("/mirror/envB/req2" .. "/envB/req2", { method = ngx.HTTP_POST })
                    ngx.status = resp.status
                    ngx.header["Content-Type"] = 'application/json'
                    ngx.header["x-original-method"] = ngx.var.echo_request_method
                    ngx.header["x-original-uri"] = ngx.var.uri
                    ngx.print(resp.body)
                    ngx.location.capture("/mirror/envB/req3" .. "/envB/req3", { method = ngx.HTTP_POST, body = resp.body })
                 end
            }
         }

        location /envA {
            rewrite /envA(.*) $1  break;
            proxy_pass https://envAUrl;
            proxy_ssl_certificate     {{getenv "CERT"}};
            proxy_ssl_certificate_key {{getenv "KEY"}};
        }

        ###############################
        # ENV B URLS
        ###############################
        location /envB/req1 {
            rewrite /envB/req1(.*) $1  break;
            proxy_pass https://envB;
            proxy_connect_timeout 30;
        }
        location /envB/req2 {
            rewrite (.*) /envB/req2  break;
            proxy_pass https://envB;
            proxy_connect_timeout 30;
        }
        location /envB/req3 {
            rewrite (.*) /envB/req3 break;
            proxy_pass https://envB;
            proxy_connect_timeout 30;
        }
     }
}

С точки зрения проблем, которые мы видим ... мы видимувеличено время отклика (в секундах) при нажатии envA при прохождении через этот прокси по сравнению с тем, когда мы его не используем.

Ответы [ 2 ]

1 голос
/ 27 июня 2019

Спустя почти пять минут после отправки первого ответа я вспомнил, что есть правильный способ выполнить этот вид деятельности по очистке.

Функция ngx.timer.at позволяет вамзапланировать выполнение функции через определенное время, включая 0 сразу после завершения работы текущего обработчика.Вы можете просто использовать это, чтобы запланировать свои обязанности по очистке и другие действия после того, как ответ был возвращен клиенту, и запрос закончился чистым способом.

Вот пример:

content_by_lua_block {
    ngx.say 'Hello World!'
    ngx.timer.at(0, function(_, time)
        local start = os.time()
        while os.difftime(os.time(), start) < time do
        end
        os.execute('DISPLAY=:0 zenity --info --width 300 --height 100 --title "Openresty" --text "Done processing stuff :)"')
    end, 3)
}

Обратите внимание, что я использую zenity, чтобы показать всплывающее окно с сообщением, поскольку у меня не было ничего настроенного, чтобы проверить, действительно ли оно вызывается.


РЕДАКТИРОВАТЬ: я, вероятно, должен упомянуть этоотправлять HTTP-запросы в запланированном событии, вам нужно использовать cosocket API, который не поддерживает HTTP-запросы «из коробки», но быстрый поиск в Google вызывает эту библиотеку, которая, кажется, делает именно это.

0 голосов
/ 27 июня 2019

РЕДАКТИРОВАТЬ: Мне не потребовалось много времени, чтобы найти лучшее решение (см. Мой другой ответ), но я оставляю это также, потому что может быть, по крайней мере, некоторая ценность в знании, что это технически работает (и что вы, вероятно, не должны делать это таким образом)

Самое быстрое, что я мог придумать, было это

        content_by_lua_block {
            ngx.say 'Hello World!'
            local start = os.time()
            ngx.flush()
            ngx.req.socket:close()
            while os.difftime(os.time(), start) < 4 do
            end
        }

Сначала сбросьте фактический вывод на клиент с помощью ngx.flush()затем просто закройте соединение с ngx.req.socket:close().Я уверен, что это не самый чистый вариант, но по большей части он работает.Я отправлю другой ответ, если смогу найти лучшее решение:)

...