Насколько хорошо вы это знаете? - Передовой опыт технической команды Alibaba

Жэнь Сицзюнь (Ren Xijun) - член технологической группы промежуточного программного обеспечения Alibaba. Недавно он столкнулся с проблемой с сервером связи на стороне клиента, который постоянно генерировал исключение. Но, к его ужасу, несмотря на поиск информации в Интернете и неоднократные попытки определить причину, он не смог найти ничего, что помогло бы объяснить наличие двух очередей или то, как наблюдать за их показателями.

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

Раздражающая проблема

В Java клиент и сервер использовали сокет для связи. В этом случае использовался сервер NIO. Произошел следующий статус:

· Периодически выполнялось трехстороннее рукопожатие для установления соединения между клиентом и сервером, но прослушивающий сокет не ответил.

· Проблема тогда же возникла во многих других соединениях.

· Селектор NIO не был уничтожен и воссоздан. Тот, который использовали, всегда был первым.

· Проблемы возникали при запуске программы и периодически возникали в дальнейшем.

Резюме: как работает трехстороннее рукопожатие TCP?

Первое, что я сделал, - это напомнил себе о стандартном процессе трехстороннего рукопожатия при установлении TCP-соединения. Стандартный процесс происходит следующим образом:

1. Клиент отправляет на сервер SYN-пакет, чтобы инициировать рукопожатие.

2. После получения этого сообщения сервер отправляет клиенту пакет SYN-ACK.

3. Наконец, клиент отправляет серверу пакет ACK, чтобы указать, что он получил пакет SYN-ACK сервера. (К этому моменту соединение с сервером уже установлено через порт 56911 клиента.)

Быстрое исправление

Судя по описанию проблемы, это было похоже на то, когда очередь завершения TCP-соединения (или очередь приема, о которой будет сказано ниже) заполнена во время установления TCP-соединения. Чтобы убедиться в этом, я проверил статистику переполнения очереди через netstat -s | egrep «слушай».

667399 times the listen queue of a socket overflowed

Проверив три раза, я обнаружил, что значение постоянно увеличивается. Было ясно, что очередь приема на сервере переполнилась.

Тогда можно было увидеть, как ОС борется с переполнением.

# cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0

Если tcp_abort_on_overflow равен 0, если очередь приема заполнена на третьем этапе трехстороннего рукопожатия, сервер отбрасывает пакет ACK, отправленный клиентом, поскольку он предполагает, что соединение не было установлено на стороне сервера.

Чтобы доказать, что исключение было связано с полной очередью соединений, я сначала изменил tcp_abort_on_overflow на 1. Если полная очередь соединений была заполнена на третьем шаге, сервер отправил бы клиенту пакет сброса, указывая, что он должен завершить оба процесс рукопожатия и соединение. (На самом деле соединение не было установлено на стороне сервера.)

Затем я продолжил тест и обнаружил, что в клиенте было несколько исключений «сброс соединения одноранговым узлом». Мы пришли к выводу, что полное переполнение очереди подключений, в свою очередь, привело к ошибке клиента, что помогло нам быстро выявить ключевые части проблемы.

Команда разработчиков просмотрела исходный код Java и обнаружила, что значение по умолчанию для невыполненной работы сокета было 50 (это значение контролирует размер полной очереди подключений и будет подробно описано позже). Я увеличил значение и запустил его снова, и после более чем 12 часов стресс-тестирования я заметил, что ошибка больше не появляется и переполнение также не увеличивается.

Итак, это так просто. После трехстороннего рукопожатия TCP происходит полное переполнение очереди соединений, и только после входа в эту очередь сервер может переключиться с Listen на accept. Значение невыполненной работы по умолчанию - 50, что легко переполнить. В случае переполнения на третьем этапе установления связи сервер игнорирует пакет ACK, отправленный клиентом. Сервер будет повторять второй шаг (отправка пакета SYN-ACK клиенту) через равные промежутки времени. Если соединение не поставлено в очередь, возникает исключение.

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

Углубляясь в подробности: процесс установления связи TCP и очереди

Как показано выше, есть две очереди: очередь SYN (или очередь неполного подключения) и очередь приема (или очередь полного подключения).

При трехстороннем рукопожатии после получения пакета SYN от клиента сервер помещает информацию о соединении в очередь SYN и отправляет пакет SYN-ACK обратно клиенту.

Затем сервер получает пакет ACK от клиента. Если очередь приема не заполнена, вам следует либо удалить информацию из очереди SYN и поместить ее в очередь приема, либо выполнить в соответствии с инструкциями tcp_abort_on_overflow.

На этом этапе, если очередь приема заполнена и tcp_abort_on_overflow равен 0, сервер снова отправляет клиенту пакет SYN-ACK через определенный период времени (другими словами, он повторяет второй этап рукопожатия). Если клиент испытывает даже короткий тайм-аут, легко может возникнуть исключение клиента.

В нашей ОС второй шаг по умолчанию повторяется дважды (пять раз для CentOS).

net.ipv4.tcp_synack_retries = 2

Новый подход

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

Команды

netstat –s

[root@server ~]#  netstat -s | egrep "listen|LISTEN" 
667399 times the listen queue of a socket overflowed
667399 SYNs to LISTEN sockets ignored

Здесь, например, 667399 указывает, сколько раз очередь приема переполнялась. Выполняйте эту команду каждые несколько секунд, и если число увеличивается, очередь приема должна быть заполнена.

команда ss

[root@server ~]# ss -lnt
Recv-Q Send-Q Local Address:Port  Peer Address:Port 
0        50               *:3306             *:*

Здесь значение Send-Q во втором столбце равно 50, что указывает на то, что очередь приема на порту прослушивания (третий столбец) составляет не более 50. Первый столбец, Recv-Q, указывает объем очереди приема, которая используется в настоящее время.

Размер очереди приема зависит от min (backlog, somaxconn). Бэклог передается при создании сокета, а somaxconn является системным параметром уровня ОС.

На этом этапе мы можем установить контакт с нашим кодом. Например, когда Java создает ServerSocket, он позволяет вам передать значение невыполненной работы.

Размер очереди SYN зависит от max (64, / proc / sys / net / ipv4 / tcp_max_syn_backlog) и ОС разных версий могут отличаться.

команда netstat

Send-Q и Recv-Q также можно отобразить с помощью команды netstat, как и с помощью команды ss. Однако, если соединение не находится в состоянии Listen, Recv-Q означает, что полученные данные все еще находятся в кэше и не были прочитаны процессом. Это значение представляет байты, которые не были прочитаны процессом. Отправить - это количество байтов в очереди отправки, которые не были подтверждены удаленным хостом.

$netstat -tn  
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address   Foreign Address State  
tcp0  0 100.81.180.187:8182  10.183.199.10:15260 SYN_RECV   
tcp0  0 100.81.180.187:43511 10.137.67.18:19796  TIME_WAIT   
tcp0  0 100.81.180.187:2376  100.81.183.84:42459 ESTABLISHED

Важно отметить, что данные Recv-Q, отображаемые командой netstat -tn, не имеют ничего общего с очередью приема или очередью SYN. Это следует подчеркнуть здесь, чтобы не путать их с данными Recv-Q, отображаемыми ss -lnt.

Например, следующий netstat -t видит, что Recv-Q накопил много данных, что обычно вызвано сбоями обработки ЦП.

Процесс проверки

Чтобы проверить информацию, подробно описанную выше, измените значение невыполненной работы в Java на 10 (чем меньше значение, тем легче будет переполнение), и продолжите выполнение стресс-тестирования. Затем клиент начинает сообщать об исключении, после чего с помощью команды ss на сервере можно наблюдать следующее.

Fri May  5 13:50:23 CST 2017
Recv-Q Send-QLocal Address:Port  Peer Address:Port
11         10         *:3306               *:*

Здесь мы видим, что очередь приема службы на порту 3306 составляет не более 10, но теперь в очереди 11 соединений. Должна быть очередь, которая не может быть поставлена ​​в очередь и будет переполняться. В то же время верно, что величина переполнения постоянно увеличивается.

Принять размер очереди в Tomcat и Nginx

Tomcat по умолчанию использует временное соединение. В Ali-Tomcat значение по умолчанию для невыполненной работы (которое в Tomcat - «счетчик принятия») - 200. В Apache Tomcat это 100.

#ss -lnt
Recv-Q Send-Q   Local Address:Port Peer Address:Port
0       100                 *:8080            *:*

В Nginx значение невыполненной работы по умолчанию - 511.

$sudo ss -lnt
State  Recv-Q Send-Q Local Address:PortPeer Address:Port
LISTEN    0     511              *:8085           *:*
LISTEN    0     511              *:8085           *:*

Nginx работает в многопроцессорном режиме, поэтому существует несколько номеров 8085, что означает, что несколько процессов прослушивают один и тот же порт, чтобы избежать переключения контекста и повысить производительность.

Резюме

Когда происходит переполнение, состояние процессора и потока выглядит нормально, но нагрузка не увеличивается. С точки зрения клиента, время ответа (сеть + очередь + время обслуживания) велико, но, учитывая истинное время обслуживания в журнале сервера, на самом деле оно очень короткое. Значение невыполненной работы по умолчанию в некоторых фреймворках, таких как JDK и Netty, невелико, что в некоторых случаях может привести к проблемам с производительностью.

Я надеюсь, что эта статья поможет вам понять концепции, принципы и функции очереди SYN и очереди приема при установлении TCP-соединения. Проблемой переполнения с очередями приема и очередями SYN легко пренебречь, но она критична, особенно в сценариях, где используются временные соединения (например, Nginx и PHP, хотя они также поддерживают постоянные соединения).

(Оригинальная статья Жэнь Сицзюнь 任喜军)

Alibaba Tech

Подробная информация о последних технологиях Alibaba из первых рук → Facebook: Alibaba Tech. Twitter: « AlibabaTech ».

Ссылка

Http://veithen.github.io/2014/01/01/how-tcp-backlog-works-in-linux.html

Http://www.cnblogs.com/zengkefu/p/5606696.html

Http://www.cnxct.com/something-about-phpfpm-s-backlog/

Http://jaseywang.me/2014/07/20/tcp-queue-%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98/

Http://jin-yang.github.io/blog/network-synack-queue.html#

Http://blog.chinaunix.net/uid-20662820-id-4154399.html