Говоря о поддержке tcp в nginx из-за сбоя потери пакетов

TCP/IP

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, чтобы закрыть соединение.