1. Неудача
Базовая архитектура показана на рисунке: клиент инициирует http-запрос к nginx, nginx перенаправляет запрос на шлюз, а шлюз направляет запрос на внутренний микросервис.
Феномен сбоя заключается в том, что каждые десять минут или несколько часов клиент получает одну или несколько последовательных ошибок тайм-аута запроса. Проверьте журнал nginx, соответствующий запрос возвращает 499; проверьте журнал шлюза, соответствующий запрос не получен.
Судя по анализу журнала, проблема должна быть в nginx или spring-cloud-gateway.
версия nginx: 1.14.2, версия весеннего облака: Greenwich.RC2.
Основная конфигурация nginx выглядит следующим образом:
[root@wh-hlwzxtest1 conf]# cat nginx.conf
worker_processes 8;
events {
use epoll;
worker_connections 10240;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
#gzip on;
upstream dbg2 {
server 10.201.0.27:8888;
keepalive 100;
}
server {
listen 80;
server_name localhost;
charset utf-8;
location /dbg2/ {
proxy_pass http://dbg2/;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
Для повышения производительности nginx отправляет шлюзу запрос http 1.1, а соединение tcp можно использовать повторно.
2. Расследование
1. Просмотр TCP-соединения
[root@10.197.0.38 logs]# ss -n | grep 10.201.0.27:8888
tcp ESTAB 0 0 10.197.0.38:36674 10.201.0.27:8888
tcp ESTAB 0 0 10.197.0.38:40106 10.201.0.27:8888
[root@10.201.0.27 opt]# ss -n | grep 10.197.0.38
tcp ESTAB 0 0 ::ffff:10.201.0.27:8888 ::ffff:10.197.0.38:40106
tcp ESTAB 0 0 ::ffff:10.201.0.27:8888 ::ffff:10.197.0.38:39266
Вы можете видеть, что между nginx и шлюзом установлено сокетное соединение (10.201.0.27:8888, 10.197.0.38:40106), а две другие записи подозрительны. Угадайте, причина в следующем: один конец ненормально закрывает TCP-соединение, но не уведомляет другой конец, или уведомляет противоположный конец, но противоположный конец не получает его.
2. Анализ захвата пакетов
Сначала посмотрите на данные захвата пакетов nginx:
Серийный номер 8403: перенаправить http-запрос на шлюз;
Серийный номер 8404: Пакет подтверждения не получен в течение времени RTT, и пакет отправлен повторно;
Серийный номер 8505: RTT около 0,2 с, повторная передача tcp;
Серийный номер 8506: 0.4s не получил пакет ack, повторная передача tcp;
Серийный номер 8507: 0.8s не получил пакет ack, повторная передача tcp;
Серийный номер 8509: 1.6s не получил пакет ack, повторная передача tcp;
...
Серийный номер 8439: 28,1 с (128RTT) не получен пакет подтверждения, повторная передача tcp.
Порядковый номер 8408: запрос имеет тайм-аут 3 с, поэтому отправляется пакет FIN.
Посмотрите на данные захвата пакетов шлюза:
Серийный номер 1372: 17:24:31 получил пакет подтверждения подтверждения, отправленный nginx, что соответствует серийному номеру 1348 на картинке захвата пакета nginx (время сервера nginx почти на 1 минуту и 30 секунд быстрее);
Серийный номер 4221: через 2 часа отправьте tcp keep-alive heartbeat-сообщение (из диаграммы захвата пакетов nginx также видно, что tcp-соединение бездействует в течение этих 2 часов);
Серийный номер 4253: повторная отправка подтверждения активности TCP через 75 с;
Серийный номер 4275: повторная отправка пульса через 75 с;
9 раз подряд;
Порядковый номер 4489: Отправьте пакет RST для сброса соединения через одноранговый узел.
2 часа, 75 секунд, 9 раз, настройка системы по умолчанию.
[root@eureka2 opt]# cat /proc/sys/net/ipv4/tcp_keepalive_time
7200
[root@eureka2 opt]# cat /proc/sys/net/ipv4/tcp_keepalive_intvl
75
[root@eureka2 opt]# cat /proc/sys/net/ipv4/tcp_keepalive_probes
9
Конкретные функции этих параметров см. в статье:Зачем приложениям на основе TCP нужны пакеты пульса?
3. Анализ
Приведенный выше анализ захвата пакетов в основном подтверждает, что проблема заключается в nginx. В 19:25 шлюз отправляет на сервер nginx tcp keep-alive heartbeat-пакет, в это время tcp-соединение зарезервировано на сервере, но ответа нет, в 22:20 nginx отправляет http-запрос на шлюз, а у шлюза tcp соединение закрыто, поэтому ответа нет.
3. Решение
1. proxy_send_timeout
Конфигурация тайм-аута, связанная с восходящим потоком в nginx, в основном имеет следующие параметры, см.:Подробное объяснение настройки тайм-аута Nginx
proxy_connect_timeout: время ожидания соединения между nginx и вышестоящим сервером;
proxy_read_timeout: nginx получает тайм-аут данных вышестоящего сервера, по умолчанию 60 с, если 1 байт не получен в течение непрерывных 60 с, соединение закрывается;
proxy_send_timeout: тайм-аут для nginx для отправки данных на вышестоящий сервер, по умолчанию 60 с, если 1 байт не будет отправлен в течение 60 с, соединение будет закрыто.
Все эти параметры относятся к уровню протокола http. Например, proxy_send_timeout = 60s не означает, что если в течение 60s не будет отправлено ни одного http-запроса, то соединение будет закрыто, это означает, что после отправки http-запроса, в течение двух операций записи, если оно превысит 60s, соединение будет закрыто. Так что эти параметры явно не то, что нам нужно.
2. Параметр keepalive_timeout вышестоящего модуля
Проверьте официальную документацию веб-сайта,Module ngx_http_upstream_module,
Syntax: keepalive_timeout timeout;
Default:
keepalive_timeout 60s;
Context: upstream
This directive appeared in version 1.15.3.
Sets a timeout during which an idle keepalive connection to an upstream server will stay open.
Настройте TCP-соединение на закрытие после простоя более 60 секунд, что нам и нужно.
Чтобы использовать этот параметр, обновите версию nginx до 1.15.8, файл конфигурации выглядит следующим образом:
http {
upstream dbg2 {
server 10.201.0.27:8888;
keepalive 100;
keepalive_requests 30000;
keepalive_timeout 300s;
}
...
}
Установите TCP-соединение для запуска 30000 http-запросов или бездействия в течение 300 секунд, затем закройте соединение.
После продолжения теста потери пакетов обнаружено не было.
Порядковый номер 938: после 5 минут простоя nginx активно инициирует сообщение FIN, чтобы закрыть соединение.