Тайм-аут шлюза с облачным шлюзом Spring и Nginx в качестве обратного прокси-сервера

Я создал шлюз API для своего приложения, и он будет действовать как передний контроллер для других микросервисов. В моей производственной настройке я использую Nginx в качестве обратного прокси-сервера для своего шлюза.

Шлюз API работает на порту 8080.

Nginx настроен следующим образом:

шлюз-api.conf:

server {
    listen 80;
    server_name api.example.com;
    location / {
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass http://localhost:30010/;
        keepalive_timeout 500s;
    }
    keepalive_timeout 500s;
    access_log /var/log/nginx/api.log;  
    error_log /var/log/nginx/api_error.log;
}

Настройка таймаута в nginx.conf:

proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
send_timeout 300;

Файл градиента Spring Cloud Gateway:

compile('org.springframework.cloud:spring-cloud-starter-gateway')
 compile('org.springframework.cloud:spring-cloud-starter-openfeign')
 compile("org.springframework.boot:spring-boot-starter-actuator")
 compile('org.springframework.boot:spring-boot-starter-security')

springBootVersion=2.0.3.RELEASE
springDMPVersion=1.0.4.RELEASE
springPlatformBomVersion=Cairo-SR2
springCloudVersion=Finchley.RELEASE

Приложение шлюза:

@SpringBootApplication
@ComponentScan(basePackages = {"com.example"})
@EntityScan(basePackages = {"com.example"})
@EnableFeignClients(basePackages = "com.example")
public class GatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

Постановка задачи:

В одном из моих микросервисов выполнение одного REST API занимает более 3 минут. Если я вызову этот API через nginx(api.example.com), он выдаст ошибку ровно через 1 минуту и выдаст HTTP-статус 504.

завиток:

curl --request GET \
  --url http://api.example.com/hellomicroservice/api/take/moretime

ошибка:

504 Timeout while reading the response from Server

Журналов ошибок в nginx или шлюзе API нет.

Лог доступа из nginx:

203.129.213.102 - - [01/Apr/2019:08:14:33 +0000] "GET hellomicroservice/api/take/moretime HTTP/1.1" 499 0 "-" "PostmanRuntime/7.3.0"

Но когда я делаю вызов того же API непосредственно к шлюзу (на порту шлюза 8080), запрос обрабатывается успешно.

curl с портом шлюза:

curl --request GET \
  --url http://api.example.com:8080/hellomicroservice/api/take/moretime

Редактировать:

Если я применяю настройки времени ожидания Nginx менее 60 секунд (например, 30 секунд), время ожидания запроса истекает через указанный интервал времени. Но если я установлю время ожидания Nginx более 60 секунд, скажем, 300 секунд, время ожидания запроса истекает через 60 секунд.

какой журнал ошибок на шлюзе API и журнал ошибок nginx, когда вы curl через обратный прокси?

Thanh Nguyen Van 01.04.2019 09:51

Нет журналов ошибок в nginx и шлюзе, добавлены журналы доступа

Nitin 01.04.2019 10:20

вы можете попробовать добавить proxy_read_timeout 300s; в блоке сервера server { proxy_read_timeout 300s;

Radha Mohan Maheshwari 01.04.2019 11:05

@RadhaMohanMaheshwari Пробовал, не работает

Nitin 01.04.2019 11:14

вы можете попробовать заменить localhost: 8080 в настройках nginx на api.example.com: 8080?

hudi 04.04.2019 15:44

В производственном развертывании мы используем AWS EKS, и мы не получаем эту проблему в производстве с теми же файлами конфигурации.

Nitin 08.01.2020 12:05
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
17
6
7 561
4

Ответы 4

Кажется, что тайм-ауты запросов не являются для вас проблемой. Это тайм-аут соединения. Я думаю, нам нужно посмотреть на заголовок

Connection

Насколько я знаю, заголовок Connection определяет, что соединение должно быть постоянным или кто имеет право поддерживать/закрывать его. Если соединение было keep-alive , то соединение будет постоянным. Для поддерживающих соединений клиент время от времени отправляет TCP-пинг, чтобы убедиться, что сервер все еще активен и удерживает соединение. В соответствии с завиток это время по умолчанию составляет каждые 60 секунд.

Теперь nginx необходимо настроить для приема соединений и поддержания его активности некоторое время с помощью директивы keepalive_timeout. Если этого нет, то nginx не будет поддерживать связи живыми.

Это должно быть причиной того, что nginx пишет в логах 499. HTTP499 — это ошибка cutom в nginx, которая говорит о том, что клиент закрыл соединение. В вашем случае curl закрыл его. Почему curlзакрыл? потому что nginx не ответил на 60-секундный TCP-пинг, так как не включена поддержка активности.

Добавление keepalive_timeout к ~ 500 или более высокому значению, чем время ожидания приложения, должно решить вашу проблему.

Теперь, почему это работало с котом напрямую? Я думаю, что Spring позволяет живому тайм-ауту быть бесконечным или очень высоким значением. Обычно в tomcat также 60 секунд.

Надеюсь, это решит вашу проблему.

Настройка keepalive_timeout 500; не сработала, та же ошибка Когда я проверил заголовок ответа, я нашел заголовок соединения как Connection →close

Nitin 08.04.2019 11:12

Установите как keepalive_timeout 500s; Держите его в разделе сервера или в локации. Я думаю, что единица 's' должна быть там.

Kris 08.04.2019 11:15

попробовал после ввода раздела сервера и местоположения с keepalive_timeout 500s;, но та же ошибка

Nitin 08.04.2019 11:39

Может быть, вам стоит попробовать вариант --no-keepalive в curl. Я предполагаю, что Nginx не соблюдает постоянное соединение. Вы также можете проверить то же самое с некоторыми другими клиентами, такими как http-client/Fiddler или любым другим, если на то пошло. Использование keep-alive полезно только в том случае, если вы выполняете несколько операций с одним и тем же соединением.

Kris 08.04.2019 13:33
Connection просто сообщает исходному серверу, что делать с TCP-сокетом после завершения ответа, идея заключается в том, что клиент будет отправлять дальнейшие запросы по потоку. В данном случае это будет между curl/postman и nginx. Я думаю, что более вероятно, что nginx просто надоедает и убивает запрос, что странно, так как read_timeout составляет 5 минут.
stringy05 10.04.2019 01:25

Keepalive может быть не включен для восходящего потока из-за того, что в вашей конфигурации отсутствует ключ proxy_http_version.

Цитата: https://www.nginx.com/blog/tuning-nginx/#proxy_http_версия

To enable keepalive connections to upstream servers you must also include the following directives in the configuration:

proxy_http_version 1.1;
proxy_set_header Connection "";

Я бы также сохранил keepalive_timeout в конфигурации, как предложил Крис.

Возможно, у вас проблема с форматированием. Примеры конфигураций показывают, что блок «сервер» обычно заключен в блок «http { }», а ключ «keepalive_timeout» обычно является дочерним элементом блока http, а не блока сервера или местоположения. Вы можете проверить свои другие ключи, чтобы убедиться, что все они также находятся под правильным родителем. Кроме того, у вас есть несколько активных конфигураций nginx в каталоге конфигурации?

TheJeff 10.04.2019 16:12

Попробуйте поместить свои настройки тайм-аута в /etc/nginx/conf.d/timeout.conf (если его там нет, создайте его). Установите ниже настройки,

proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;

Я предполагаю, что это одна из проблем, которые могут возникнуть из-за многих других вещей. Это решение сработало для меня (я также получал ошибки в /var/log/nginx/error.log:

2020/12/30 21:47:47 [error] 26765#26765: *13064 upstream timed out (110: Connection timed out) while connecting to upstream, client: XXX, server: example.com, request: "GET /eshop HTTP/1.0", upstream: "http://[::1]:8080/error_50x.html", host: "example.com"

Как ни странно, этого не происходило на моем ноутбуке, а только на моем сервере. Итак, я проверил IP-адреса, и оказалось, что это может быть из-за отсутствия адреса :: 1. Когда я добавил его к сетевому устройству вот, я не смог воспроизвести тайм-ауты.

sudo ip a add ::1/128 dev lo

Далее: (это мое понимание, я не уверен в этом на 100%:) Кроме того, поскольку накладные расходы на сохранение соединения с службой Java локального хоста кажутся выше, чем просто разрыв этого соединения и повторное соединение при выполнении другого запроса, рекомендуется использовать следующие настройки для прокси (в .conf вашего сайта nginx):

proxy_http_version 1.1;
proxy_set_header Connection "";

См. https://stackoverflow.com/a/10396874/3223505

Другие вопросы по теме