"Что-то не так с сетью. Раньше у меня было 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 пути