В начале 1980-х годов стек TCP/IP стал доминирующим протоколом, оставив Telnet для более специализированных целей, наконец-то появились настройки, позволяющие оптимизировать поток трафика и избежать перегрузок и потери данных. Однако даже сейчас бывает сложно понять, когда и как использовать эти настройки. В этой статье расмотрим некоторые методы оптимизации TCP, в частности Алгоритм Нейгла, TCP_NODELAY, Delayed ACK и TCP_QUICKACK.
Алгоритм Нейгла
Алгоритм Нейгла, названный в честь его создателя Джона Нейгла, является одним из механизмов повышения эффективности TCP за счет уменьшения количества маленьких пакетов, передаваемых по сети. Цель заключалась в том, чтобы предотвратить передачу большого количества маленьких пакетов, если приложение доставляет данные в сокет довольно медленно. Если процесс передает много маленьких пакетов, он может создавать излишнюю нагрузку на сеть. Это особенно верно, если полезная нагрузка пакета меньше, чем данные заголовка TCP.
Документ Нейгла, Управление перегрузкой сетей IP/TCP (RFC 896) описывает то, что он назвал «проблемой небольших пакетов», которая заключается в том, что приложение неоднократно посылает данные небольшими порциями, часто размером в 1 байт. Так как TCP-пакеты имеют 40 байт заголовка (20 байт TCP, 20 байт IPv4), это приводит к тому, что передается пакет размером 41 байт, несущий в себе 1 байт полезной информации, то есть к огромным накладным расходам.
Алгоритм Нейгла работает путём объединения нескольких небольших исходящих сообщений, а затем отправки их всех сразу. В частности, пока существует отправленный пакет, для которого отправитель ещё не получил никакого подтверждения о доставке, отправитель должен держать в буфере следующие данные для отправки, до тех пор, пока не наберётся достаточно данных на полный пакет, который можно отправить единожды.
Задержка подтверждения TCP или Delayed ACK
Задержка подтверждения TCP или Delayed ACK - это еще одна техника, используемая некоторыми реализациями TCP в попытке улучшить производительность сети и уменьшить перегрузку. Delayed ACK был изобретен для уменьшения количества ACK, необходимых для подтверждения сегментов, и снижения накладных расходов протокола. Delayed ACK - это задержка сегмента ACK получателем на время действия таймера delayed ACK, примерно 200 - 500 мс. Задержка ACK означает, что TCP не сразу подтверждает каждый полученный сегмент TCP. Несколько ответов ACK могут быть объединены в один ответ, что снижает накладные расходы протокола. Delayed ACK - это, по сути, ставка, сделанная получателем на 200-500 мс, что новый пакет придет до истечения таймера. Хотя при некоторых обстоятельствах эта техника может привести к снижению производительности приложения.
Алгоритм Нейгла и Delayed ACK не очень хорошо работают вместе в сети TCP/IP
Алгоритм Нейгла и Delayed ACK были созданы примерно в одно и то же время, но из-за отсутствия сотрудничества между создателями они предоставляли неполные и иногда противоречивые решения. Сам Джон Нейгла выразил разочарование по поводу сложившейся ситуации в теме на Hacker News, сказав:
Это все еще раздражает меня. Настоящая проблема заключается не в предотвращении tinygram. Проблема в задержках ACK и в этом дурацком фиксированном таймере. Они оба появились в TCP примерно в одно и то же время, но независимо друг от друга. Я занимался предотвращением tinygram (алгоритм Nagle), а Беркли - delayed ACKs, оба в начале 1980-х. Сочетание этих двух способов ужасно.
Delayed ACK пытается отправить больше данных за сегмент, если это возможно. Но часть алгоритма Нейгла зависит от ACK для отправки данных. Алгоритм Нейгла и Delayed ACK вместе создают проблему, потому что Delayed ACK ожидает отправки ACK, в то время как алгоритм Нейгла ожидает получения ACK! Это создает случайные задержки в 200-500 мс на сегментах, которые в противном случае могли бы быть отправлены немедленно.
В TCP отсутствует возможность автоматически отключить алгоритм Нейгла или Delayed ACK, поэтому вы должны достаточно хорошо понимать свою сеть, чтобы выбрать опции, которые обеспечат наилучшую производительность.
Что такое TCP_NODELAY
и TCP_QUICKACK
?
Очень важно понимать взаимодействие между алгоритмом Нейгла и Delayed ACK. Опция сокета TCP_NODELAY
позволяет вашей сети обходить задержки Нейгла, отключая алгоритм Нейгла и отправляя данные, как только они становятся доступны. Включение TCP_NODELAY
заставляет сокет отправлять данные, находящиеся в его буфере, независимо от размера пакета. Чтобы отключить алгоритм буферизации Нейгла, используйте параметр сокета TCP_NODELAY
. Чтобы отключить Delayed ACK, используйте опцию сокета TCP_QUICKACK
.
Три основных довода в пользу использования по умолчанию опции TCP_NODELAY
, отключающей алгоритм Нейгла
setsockopt(descriptor, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
-
Несовместимость алгоритма Нейгла с оптимизацией Delayed ACK, при которой ACK-ответ отправляется не сразу, а после получения ответных данных. Проблема в том, что в алгоритме Нейгла поступление ACK-пакета является сигналом для отправки агрегированных данных, а если ACK-пакет не поступил, отправка выполняется при наступлении таймаута. Таким образом, возникает замкнутый круг и ACK-пакет как сигнал не работает, так как другая сторона не получает данные из-за их накопления на стороне отправителя, а отправитель не отправляет их до таймаута, так как не получает ACK-пакет.
-
RFC для алгоритма Нейгла принят в 1984 году и он не рассчитан на параметры современных высокоскоростных сетей и серверов в датацентрах, что приводит к возникновению проблем с отзывчивостью. Задержка между отправкой запроса и получением ответа (RTT) в современных сетях составляет 0.5 мс + несколько миллисекунд при обмене данными между датацентрами в одном регионе + до сотни миллисекунд при отправке по всему миру. За эти миллисекунды современный сервер способен выполнить огромный объём работы.
-
Современные распределённые приложения давно не отправляют единичные байты данных, а агрегирование мелких данных обычно реализуется на уровне приложения. Даже если размер полезных данных составляет 1 байт, то, как правило, фактически размер отправляемой информации существенно возрастает после применения сериализации, использования API-обвязок в JSON и отправки с использованием TLS-шифрования. Экономия 40 байтов становится не столь актуальной.
Источники
Комментарии в Telegram-группе!