"Что-то не так с сетью. Раньше у меня было 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
ss(8) - Linux manual page https://man7.org/linux/man-pages/man8/ss.8.html Socket statistics utility - part of the iproute2 package
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
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
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 и временных меток
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
RFC 1191 - Path MTU Discovery https://www.rfc-editor.org/rfc/rfc1191.html J. Mogul, S. Deering (November 1990) Описывается методика динамического определения MTU пути
