AI [Перевод] Решение проблем низкой скорости передачи данных по TCP: подход на уровне стека

Регистрация
23 Август 2023
Сообщения
3 641
Лучшие ответы
0
Реакции
0
Баллы
243
Offline
#1


"Что-то не так с сетью. Раньше у меня было 4Гбит/с, а теперь выдаёт только 120Мбит/с. Ты что-то менял недавно?"

Знакомо звучит? Если вы хоть немного занимались поддержкой продуктовых сред, вам, вероятно, доводилось слышать подобные жалобы. Прежде чем прийти к выводам о причинах проблемы, нам нужно разобраться, что именно происходит на уровне TCP обоих хостов.

Чего НЕ будет в статье


Прежде чем начнём, позвольте мне внести полную ясность: это не очередное объяснение основ TCP. Я не собираюсь разбирать трёхсторонние рукопожатия, объяснять, что такое SYN/ACK или рисовать диаграммы состояний конечного автомата TCP. Есть сотни статей и RFC, которые это всё хорошо освещают.

Мы сосредоточимся на инструментах и метриках, которые действительно помогут вам диагностировать реальные проблемы.

Подход к решению проблем


Когда кто-то жалуется на медленную работу сети, ключевым моментом в диагностике проблем является понимание того, что происходит с TCP на обоих хостах. Современные реализации TCP отличаются замечательной устойчивостью и могут поддерживать хорошую пропускную способность даже при умеренных потерях пакетов (1-2%). Изучив внутреннее состояние TCP, мы сможем ответить на эти важнейшие вопросы:


  • Какую реальную скорость передачи данных достигает TCP и почему?


  • Ограничивает ли пропускную способность одна из сторон? Какая из них?


  • Корректно ли TCP подстраивается под состояние сети?


  • Случаются ли потери пакетов и как TCP исправляет это?

Ваша задача — собрать эти сведения, пользуясь предоставляемыми Linux средствами. Это означает работу с двумя основными инструментами: ss (статистика по сокетам) и nstat (сетевая статистика).

Инструмент №1: ss — состояние TCP для каждого сокета


Команда ss (заменившая более старую netstat) даёт детальную информацию об отдельных TCP-соединениях. Вся магия происходит когда вы используете флаг -i (information):

ss -tin dst 3.130.241.210:443


Здесь показано внутреннее состояние TCP для конкретного соединения. Вот как выглядит нормальная передача данных:

State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 172.17.1.187:47128 3.130.241.210:443
cubic wscale:12,7 rto:236 rtt:35.047/19.403 ato:40 mss:1288 pmtu:1500 rcvmss:1288 advmss:1448 cwnd:10 bytes_sent:102830 bytes_acked:102831 bytes_received:169536 segs_out:2785 segs_in:2247 data_segs_out:1571 data_segs_in:1214 send 2940052bps lastsnd:7176 lastrcv:7154 lastack:7154 pacing_rate 5879976bps delivery_rate 1401584bps delivered:1572 app_limited busy:54422ms reord_seen:6 rcv_rtt:27 rcv_space:14480 rcv_ssthresh:70646 minrtt:16.109 rcv_ooopack:2 snd_wnd:77824


Уже глаза разбегаются? Давайте разберём ключевые поля, которые вам нужно понимать.

Понимание Window Scaling


Поле wscale:12,7 (wscale: коэффициент масштабирования отправки, коэффициент масштабирования приёма) показывает коэффициенты масштабирования окна, согласованное во время установления соединения. Без масштабирования TCP ограничен окнами размером 64кБ, что значительно ограничивает пропускную способность!

Max Throughput = Window Size / RTT


Если вы видите wscale:0,0, значит, масштабирование окна не было включено с одной или с обеих сторон, либо было отключено во время установления соединения. Проверьте:

cat /proc/sys/net/ipv4/tcp_window_scaling # Должно быть 1


При wscale:7, умножьте заявленное значение окна на 2^7 (128). Для 10Гбит/с при RTT 10мс, вам требуется окно ~12.5МБ. Чтобы понять, сможет ли размер вашего окна обеспечить ожидаемую пропускную способность, посчитайте по формуле: Требуемое окно = Пропускная способность * RTT.

RTT: взгляд TCP на сетевую задержку


Поле rtt:35.047/19.403 показывает сглаженное значение RTT (35,047мс) и среднее отклонение (19,403мс). ЭТО НЕ ICMP ping — это измерение на основе реальных ACK'ов, результаты которого используются при расчёте RTO, принятии решений по управлению перегрузкой и выбору темпа изменения скорости передачи данных.

Каждый сокет поддерживает своё значение RTT!

О чём это нам говорит: Высокий RTT (>100мс в локальных сетях) или большое отклонение указывают на задержку или джиттер. Если вы видите rtt:150.5/75.2 в сети, где должно быть 1мс, проверьте, не заполнена ли Recv-Q — это говорит о том, что принимающая сторона не вычитывает данные достаточно быстро, из-за чего TCP замеряет более высокое значение RTT, ожидая открытия окна.

RTO: Retransmission Timeout


Поле rto:236 показывает текущий таймаут повторной передачи (236мс), динамически рассчитываемый на основе RTT: RTO = SRTT + max(G, 4 * RTTVAR). Понимание RTO помогает объяснить поведение при повторной передаче — слишком высокий RTO по сравнению с фактическим состоянием сети может вызывать ненужные повторные передачи данных, тогда как завышенный RTO (много выше текущего RTT) указывает на процесс восстановления TCP после предыдущих событий таймаута.

MSS и Path MTU Discovery


mss:1288 pmtu:1500 rcvmss:1288 advmss:1448


MSS (1288 байт) и PMTU (1500 байт) говорят вам о размерах пакетов на всём пути следования.

На что указывают эти значения:


  • pmtu:1500, но соединение прерывается после первоначальной передачи данных: Реальный MTU пути меньше (туннели/VPNы создают PMTU black hole)


  • Необычно маленькое значение MSS на стандартном Ethernet: Промежуточное устройство ограничивает значение MSS


  • Настроен MTU размером 9000 байт, но появляется сообщение small MSS: Где-то по пути следования трафика не поддерживаются jumbo-фреймы или через PMTU не удалось выяснить корректное значение
Инструмент №2: nstat — Общесистемные счётчики TCP


В то время как ss даёт информацию по каждому сокету, nstat показывает совокупные TCP-счётчики по всем соединениям. Это бесценно для выявления закономерностей:

nstat -az | grep -i tcp


Флаг -a показывает абсолютные значения счётчиков (вместо приращений с момента предыдущего использования), а -z — счётчики с нулевым значением. Запустите ещё раз спустя несколько секунд, чтобы увидеть изменения:

nstat -az | grep -i tcp
sleep 5
nstat -az | grep -i tcp

Retransmissions: SACK против повторных ACK'ов


Чтобы понять, происходит ли повторная передача пакетов, обратите внимание на ключевые счётчики в выводе команды nstat, являющиеся неопровержимым доказательством потери пакетов.

Ключевые счётчики:


  • TcpRetransSegs — Общее количество повторно переданных сегментов


  • TcpExtTCPSACKRecovery — Быстрая ретрансляция через SACK (современный, эффективный способ)


  • TcpExtTCPSACKReorder — Быстрое переупорядочение полученных пакетов


  • TcpExtTCPRenoRecovery — Быстрая повторная передача с помощью дублирующих подтверждений (устаревший, менее эффективный способ)


  • TcpExtTCPTimeouts — Полное истечение RTO (приводит к медленному запуску, серьезно снижает пропускную способность)

Понимание разницы: SACK сообщает отправителю какие именно пакеты потеряны, что позволяет произвести выборочную повторную передачу. Без SACK TCP полагается на три повторяющихся ACK'а и может без необходимости повторно передать данные. Проверьте статус SACK: cat /proc/sys/net/ipv4/tcp_sack (должно быть 1).

Пример из практики:

TcpRetransSegs 245
TcpExtTCPSACKRecovery 12
TcpExtTCPSACKReorder 243
TcpExtTCPTimeouts 2


Тут видно 245 повторно переданных сегмента с 12 событиями восстановления через SACK (указывает, что TCP эффективно обрабатывает потерю пакетов) и 2 полных таймаута. Кроме того, было зафиксировано 243 события изменения порядка пакетов.

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

Ещё одним случаем, который должен обрабатывать TCP, является изменение порядка сегментов, что может приводить к снижению производительности передачи данных. Высокий уровень событий такого рода указывает на доставку значительного числа пакетов в неправильном порядке, что требует от получателя буферизации и изменения последовательности пакетов перед тем, как данные будут доступны приложению. Это означает более высокую задержку и более низкую пропускную способность.

Подведение итогов: Алгоритм диагностики


Когда кто-то сообщает о низкой производительности TCP, вот мой системный подход к пониманию того, что происходит:

# Пока идёт передача данных
ss -tin dst <remote_ip>


Смотрим:


  • Включено ли масштабирование окна и адекватное ли оно? (значение wscale не должно быть равно 0,0)


  • Какое значение RTT измерил TCP?


  • Соответствуют ли значени MSS/PMTU пути следования трафика?


  • Происходит ли повторная передача? (поле retrans)


  • Send-Q или Recv-Q постоянно заполнены? (указывает, какая сторона ограничивает пропускную способность)

nstat -az | grep -i tcp; sleep 10; nstat -az | grep -i tcp


Смотрим:


  • TcpRetransSegs rate (показывает частоту повторной передачи)


  • TcpExtTCPTimeouts (указывает на серьёзную потерю пакетов или задержки)


  • TcpExtTCPSACKRecovery vs TcpExtTCPRenoRecovery (какой механизм восстановления активен)


  • TcpExtTCPSACKReorder (показывает пакеты, полученные в неправильной последовательности)


  • TcpExtTCPSACKReneging (если значение не равно нулю, получатель ведёт себя непоследовательно)

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

Общие закономерности и что они нам говорят

Случай: Кратковременно высокая пропускная способность, затем падение почти до нуля


  • Проверить: постоянная заполненость Recv-Q


  • Интерпретация: Принимающее приложение не может считывать данные достаточно быстро; управление потоком TCP ограничивает скорость отправителя
Случай: Пропускная способность ограничена независимо от полосы пропускания


  • Проверить: wscale:0,0 в выводе ss


  • Интерпретация: Масштабирование окна отключено; размер окна TCP ограничен 64КБ, пропускная способность ограничивается на основе RTT
Случай: Много повторных передач, но ping показывает минимальные потери.


  • Проверить: высокое значение TcpExtTCPTimeouts, RTO много больше текущего значения RTT


  • Интерпретация: Предыдущие события таймаута завышали RTO; TCP работает в консервативном режиме во время своего восстановления
Случай: Передача данных зависает после отправки части данных


  • Проверить: значения MSS/PMTU, соединение перестаёт отвечать


  • Интерпретация: PMTU black hole — пакеты, размер которых превышает фактический MTU пути, незаметно отбрасываются
Случай: ретрансляция преимущественно через TcpExtTCPRenoRecovery


  • Проверить: SACK выключен (tcp_sack=0)


  • Интерпретация: TCP использует старый механизм восстановления с помощью повторных ACK, что менее эффективно, чем SACK, при множественных потерях
Заметка о захвате пакетов


Я намеренно не стал рассматривать захват пакетов в этой статье, потому что он заслуживает отдельного подробного анализа. Такие инструменты, как tcpdump и Wireshark, невероятно мощны, но они также отнимают много времени и генерируют огромные объемы данных. По моему опыту, большинство проблем с производительностью TCP можно диагностировать пользуясь лишь ss и nstat.

Тем не менее, есть случаи, когда захват пакетов абсолютно необходим — особенно когда вы подозреваете помехи со стороны промежуточного устройства — вам нужно проверить поведение управления перегрузкой или вы хотите увидеть точное время событий во время установления соединения. Практический анализ захвата пакетов для устранения неполадок TCP я рассмотрю в будущей статье.

Заключение


В следующий раз, когда кто-то сообщит о низкой скорости TCP, начните с понимания того, что происходит с TCP на обоих хостах. Используйте ss для анализа состояния каждого сокета и nstat для наблюдения за общесистемными закономерностями. Обратите внимание на масштабирование окна, измерения RTT, значения RTO, параметры MSS/PMTU и случаи повторной передачи.

Эти инструменты обеспечивают прямое наблюдение за процессом принятия решений TCP и помогают ответить на важные вопросы:


  • Какую скорость обеспечивает TCP?


  • Какая сторона ограничивает пропускную способность?


  • Как TCP адаптируется к условиям сети?


  • Эффективно ли обрабатывается потеря пакетов?

Понимание внутреннего состояния TCP помогает систематично диагностировать проблемы и объясняет, что происходит "под капотом". Иногда объяснение указывает на необходимость изменений некоторых настроек, иногда выявляет особенности поведения приложений, требующие внимания, а иногда показывает, что TCP работает именно так, как должен, учитывая условия сети.

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

Какой ваш основной подход к устранению проблем с производительностью TCP? Нашли ли вы другие полезные метрики TCP? Дайте мне знать в ваших комментариях.

Ссылки

Man Pages


  1. ss(8) - Linux manual page https://man7.org/linux/man-pages/man8/ss.8.html Socket statistics utility - part of the iproute2 package


  2. nstat(8) - Linux manual page https://man7.org/linux/man-pages/man8/nstat.8.html Network statistics tool for monitoring kernel SNMP counters

IETF RFCs


  1. RFC 6298 - Computing TCP's Retransmission Timer https://www.rfc-editor.org/rfc/rfc6298.html V. Paxson, M. Allman, J. Chu, M. Sargent (June 2011) Определяет стандартный алгоритм для расчета RTO TCP


  2. RFC 7323 - TCP Extensions for High Performance https://www.rfc-editor.org/rfc/rfc7323.html D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (September 2014) Задает параметры масштабирования окна TCP и временных меток


  3. RFC 2018 - TCP Selective Acknowledgment Options https://www.rfc-editor.org/rfc/rfc2018.html M. Mathis, J. Mahdavi, S. Floyd, A. Romanow (October 1996) Определяет механизм SACK для TCP


  4. RFC 1191 - Path MTU Discovery https://www.rfc-editor.org/rfc/rfc1191.html J. Mogul, S. Deering (November 1990) Описывается методика динамического определения MTU пути
 
Яндекс.Метрика Рейтинг@Mail.ru
Сверху Снизу