Пакеты пришли не в том порядке: что такое reordering и почему приложение это чувствует
Вы снимаете дамп трафика на проблемном соединении и видите в Wireshark десятки строк с пометкой out-of-order — а на первый взгляд ничего страшного не произошло: ни один пакет действительно не потерялся, все они в итоге дошли. Но сервис при этом ощутимо тормозит, а счётчик ретрансмитов на сервере медленно растёт, хотя mtr до клиента не показывает ни одной потери. Это классическая картина packet reordering — пакеты одного потока прибыли к получателю не в том порядке, в котором были отправлены, и TCP, который был спроектирован в предположении, что порядок почти всегда сохраняется, реагирует на это так же нервно, как на настоящую потерю.
Содержание
- Почему пакеты одного потока вообще едут в разном порядке
- Как TCP интерпретирует пакет, пришедший раньше своей очереди
- Reordering маскируется под потерю — и в этом вся суть проблемы
- Современные реализации TCP стали устойчивее — но не иммунны
- Как обнаружить reordering на практике
- Что делать, если reordering всё-таки мешает
Почему пакеты одного потока вообще едут в разном порядке
В идеальной модели сети пакет A, отправленный до пакета B, приходит к получателю тоже первым — потому что оба идут по одному и тому же физическому пути с одной и той же задержкой. На практике современная сетевая инфраструктура эту идеальную модель регулярно нарушает, и делает это не по ошибке, а сознательно, ради балансировки нагрузки.
Между отправителем и получателем почти всегда стоит не один маршрутизатор, а несколько равноценных путей: провайдеры и дата-центры строят сети с избыточностью именно для этого — чтобы трафик можно было распределять по нескольким физическим каналам одновременно, а не гонять всё через одно узкое место. Механизмы вроде ECMP (equal-cost multi-path routing) на маршрутизаторах и схемы балансировки нагрузки на пограничных устройствах решают, по какому из нескольких равнозначных путей отправить конкретный пакет. В норме они стараются хешировать по одному и тому же потоку (обычно по связке адресов и портов источника/назначения), чтобы все пакеты одного TCP-соединения шли по одному пути — это снижает риск reordering, но не устраняет его полностью.
Проблема в том, что «равноценные» пути редко бывают идентичными по задержке. Один физический канал может проходить через чуть более загруженный сегмент, другой — иметь на одно транзитное соединение больше, третий — временно испытывать микрозадержки из-за переполнения буфера на промежуточном узле. Если часть пакетов потока в моменте перераспределяется на другой путь (например, при переключении маршрута или ребалансировке нагрузки), пакет, отправленный чуть позже, может обогнать в пути пакет, отправленный чуть раньше — просто потому что его путь в этот момент оказался быстрее. Получатель видит сегменты не в порядке номеров последовательности (sequence number), хотя ни один из них по дороге не потерялся.
Это отличается от асимметричной маршрутизации, где путь «туда» и путь «обратно» просто разные и это нормально — подробнее в статье про асимметричный маршрут туда и обратно. Reordering — эффект внутри одного направления, между пакетами одного потока, и обычно не значит, что маршрут «неправильный»: просто у сети было несколько путей на выбор, и для части пакетов выбор в моменте разошёлся. О том, почему сетевой маршрут вообще может не совпадать с интуитивно ожидаемым, — в статье почему маршрут не совпадает с географией.
Как TCP интерпретирует пакет, пришедший раньше своей очереди
TCP построен вокруг идеи упорядоченного потока байт: получатель ожидает сегменты по возрастанию sequence number и подтверждает получение через ACK. Когда TCP-получатель видит сегмент с номером, который «перепрыгивает» через ещё не полученный более ранний сегмент, у него ровно два возможных объяснения: либо более ранний сегмент потерялся в пути, либо он просто задержался и вот-вот придёт следом. Различить эти два случая в моменте невозможно — у получателя нет информации о том, что происходит на сети между ним и отправителем.
Классическая реакция TCP на этот пробел — не молчать, а сразу же послать отправителю duplicate ACK: подтверждение с тем же номером, что и предыдущее, сигнализирующее «жду именно этот байт, а не то, что пришло позже». Если отправитель получает подряд несколько таких дублирующихся ACK, он интерпретирует это как достаточно сильный сигнал потери и запускает fast retransmit — повторно отправляет сегмент, который получатель якобы не дождался, не дожидаясь полного таймаута ретрансмита.
Именно здесь reordering и создаёт проблему. Если пакет, которого получатель «не дождался», на самом деле просто задержался на другом, чуть более медленном пути, а не потерялся — он рано или поздно всё равно доедет. Но отправитель уже успел отреагировать на дублирующиеся ACK как на признак потери: отправил сегмент повторно и, в зависимости от реализации congestion control, мог заодно уменьшить окно перегрузки (congestion window), решив, что сеть перегружена. В результате получатель может получить один и тот же сегмент дважды — «настоящий», просто задержавшийся, и ретрансмит, спровоцированный ложной тревогой. С точки зрения полезной нагрузки это чистые накладные расходы: лишний трафик и необоснованное снижение скорости передачи из-за срабатывания механизма, рассчитанного на другую причину.
Здесь стоит подчеркнуть: эта цепочка — не гипотетическая экзотика, а прямое следствие того, как вообще устроена детекция потерь в TCP. Тот же механизм дублирующихся ACK и fast retransmit, который заставляет TCP резко реагировать на реальную потерю (это подробно разобрано в статье про потерю 0,3% пакетов и просадку скорости вдвое), одинаково легко срабатывает и на reordering — потому что на уровне «что видит получатель» эти две ситуации в первый момент неотличимы друг от друга.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверReordering маскируется под потерю — и в этом вся суть проблемы
Стоит явно проговорить главную мысль этой статьи: reordering сам по себе не теряет данные и не должен приводить к повторной передаче — все пакеты в итоге доходят, просто не в том порядке. Полезная нагрузка не страдает. Страдает то, как TCP интерпретирует происходящее, потому что механизм обнаружения потерь по дублирующимся ACK изначально был рассчитан на сеть, где reordering — редкое и случайное явление, а не системная особенность конкретного маршрута.
Отсюда и практическое последствие: на сети с заметным reordering вы можете видеть в статистике TCP растущее число ретрансмитов и дублирующихся ACK, при полном отсутствии реальной потери пакетов на пути. Это не безобидная арифметическая аномалия — каждый ложный fast retransmit это лишний трафик и потенциальное снижение congestion window, то есть реальная просадка эффективной скорости передачи, хотя формально «ничего не потерялось». Для приложения эффект ощущается как нестабильная, будто бы случайно проседающая скорость — без видимой причины в виде явных потерь на маршруте.
Есть и вторая сторона эффекта — на уровне TCP-стека получателя. Сегменты, пришедшие не по порядку, нельзя сразу отдать приложению: TCP гарантирует прикладному уровню строго последовательный поток байт, поэтому такие сегменты складываются в буфер приёма (reassembly buffer) и ждут недостающий более ранний сегмент. Пока буфер ждёт, для приложения соединение выглядит так, будто данные не поступают — своего рода head-of-line blocking на уровне транспорта, даже если весь объём уже долетел до сетевой карты получателя.
Современные реализации TCP стали устойчивее — но не иммунны
Ранние реализации TCP реагировали на первый же пакет не по порядку почти так же, как на потенциальную потерю — это делало сети с заметным reordering особенно болезненными для TCP-трафика. За прошедшие десятилетия стек в популярных операционных системах, включая Linux, заметно доработали именно в этом направлении: современные реализации умеют отличать единичный, случайный сдвиг порядка от систематической потери и не запускают fast retransmit по первому подозрительному сигналу — какое-то время они «терпят» умеренный reordering, ожидая, что задержавшийся сегмент всё же придёт, прежде чем делать вывод о реальной потере.
Здесь стоит избегать соблазна назвать точные пороги и цифры — конкретные алгоритмы детекции потерь эволюционируют, отличаются между версиями ядра и операционными системами, и то, что было верно для одной реализации несколько лет назад, может быть настроено иначе сегодня. Общий принцип устойчив и подтверждается практикой: современный TCP-стек не паникует от единичного, редкого случая reordering, и умеренный эпизодический сдвиг порядка на реальных сетях сегодня обычно проходит для производительности почти незаметно.
Проблема начинается там, где reordering перестаёт быть эпизодическим и становится систематическим свойством конкретного маршрута — например, когда балансировка нагрузки на промежуточном оборудовании стабильно раскладывает пакеты одного потока по путям с ощутимо разной задержкой, и делает это не время от времени, а постоянно. Устойчивость современного TCP снижает частоту ложных срабатываний, но не отменяет их полностью: рано или поздно накопленный сдвиг превышает терпение конкретной реализации, и цепочка «дублирующиеся ACK → fast retransmit → просадка окна» запускается снова, просто реже, чем на старых стеках. Поэтому сильный или регулярный reordering — по-прежнему реальный штраф по производительности, а не полностью решённая историческая проблема.
Как обнаружить reordering на практике
Обнаружить reordering сложнее, чем обычную потерю пакетов, именно потому, что формально ничего не теряется — стандартный тест вроде ping или замер потерь через mtr его не покажет: все пакеты доходят, просто задержка между ними иногда «перескакивает» так, что порядок нарушается.
Прямой способ увидеть reordering — захват трафика и анализ последовательности пакетов. Инструменты вроде Wireshark умеют явно размечать сегменты, пришедшие не по порядку: такие сегменты помечаются отдельной категорией экспертной информации TCP-анализа, отличной от обычных ретрансмитов, и по фильтру легко отделить «пакет пришёл не вовремя» от «пакет действительно отправлен повторно». Кроме анализаторов трафика общего назначения существует и отдельная категория специализированных инструментов и методик именно для измерения reordering в сети — они оценивают, насколько часто и насколько сильно нарушается порядок в потоке, а не просто фиксируют сам факт. Если вы подозреваете системный reordering на конкретном маршруте, стоит поискать такой инструмент под свою платформу, а не полагаться только на общую сетевую диагностику.
Косвенный, но практичный признак reordering на боевом сервере — это растущие счётчики дублирующихся ACK и ретрансмитов в TCP-статистике при том, что независимый замер потерь на пути (например через mtr) реальных потерь не показывает. Посмотреть статистику по конкретному соединению можно командой:
ss -ti state established '( dport = :443 or sport = :443 )'
а агрегированную картину по всем соединениям сервера — через:
netstat -s | grep -iE 'retrans|reorder|out.of.order'
Если ретрансмиты стабильно есть и растут вместе с трафиком, а mtr до тех же адресов на протяжении длительного наблюдения потерь не фиксирует — это весомый повод заподозрить именно reordering, а не реальную деградацию канала. Подробнее о том, как читать статистику ретрансмитов и отделять фоновый шум от системной проблемы, разобрано в статье про восемь процентов ретрансмитов из-за одного патч-корда — методика чтения счётчиков там применима и к диагностике reordering, только причина в конце расследования окажется другой: не физическое повреждение линии, а поведение балансировщика или маршрутизатора на пути.
Что делать, если reordering всё-таки мешает
Если reordering происходит на сети провайдера или транзитных операторов между вами и клиентами, повлиять на конфигурацию промежуточных маршрутизаторов вы не можете — но можно снизить чувствительность собственной инфраструктуры к проблеме и точнее локализовать источник:
- Проверьте многопутевость на своей стороне. Если у сервера несколько исходящих каналов с балансировкой (например, несколько аплинков с ECMP на пограничном маршрутизаторе), убедитесь, что хеширование потоков стабильно закрепляет каждое TCP-соединение за одним путём. Это конфигурация вашего оборудования, и она полностью в ваших руках.
- Замерьте reordering отдельно от потерь, а не полагайтесь на один общий показатель «качества сети». Канал с нулевыми потерями по
mtrвполне может иметь заметный reordering, который виден только через захват трафика или специализированные измерения. - Не спешите винить провайдера по одной жалобе пользователя. Единичный эпизод reordering современный TCP-стек, скорее всего, переживёт без последствий — искать причину стоит, когда счётчики ретрансмитов растут стабильно и коррелируют с конкретным направлением или сегментом маршрута.
- Если проблема воспроизводится стабильно у всех клиентов, проверьте и сетевую конфигурацию самого сервера — драйверы сетевой карты, оффлоады, настройки очередей: иногда источник reordering ближе, чем кажется, и находится не в интернете, а в собственной инфраструктуре.
Собственный тестовый стенд между двумя серверами в разных локациях — рабочий способ проверить гипотезу до разговора с провайдером: если между двумя вашими VPS вы стабильно видите растущие ретрансмиты без потерь по mtr, у вас на руках воспроизводимый случай, а не абстрактная жалоба «иногда тормозит».
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Reordering — это то же самое, что джиттер?
Нет, хотя причины связаны. Джиттер — изменчивость задержки между пакетами одного пути, а reordering — нарушение порядка прибытия из-за того, что разные пакеты пошли по разным путям с разной задержкой. Джиттер может существовать без reordering, а reordering почти всегда предполагает multipath-эффект того или иного рода.
Может ли reordering происходить внутри моей собственной локальной сети?
Да, если есть балансировка нагрузки, агрегация каналов (link aggregation, LACP) с неидеальным хешированием или несколько путей между узлами внутри дата-центра. Это тот случай, когда причина полностью в ваших руках и обычно решается настройкой хеширования потоков на активном оборудовании.
UDP-трафик тоже страдает от reordering?
UDP сам не заботится о порядке доставки — протокол не гарантирует ни доставку, ни порядок, поэтому reordering не создаёт лишних ретрансмитов на транспортном уровне. Но если поверх UDP работает протокол или приложение, которому порядок важен (потоковое видео, VoIP), с восстановлением порядка разбирается уже логика этого протокола, а не сам UDP.
Стоит ли пытаться зафиксировать один физический путь для каждого соединения, чтобы избежать reordering полностью?
На своей инфраструктуре — да, это разумная цель при настройке балансировки. На чужой сети, между вами и клиентами по всему интернету, повлиять на выбор пути вы обычно не можете — остаётся полагаться на устойчивость современных реализаций TCP к умеренному reordering.
Как отличить reordering от реальной, но редкой потери, если оба варианта дают похожие ретрансмиты?
Надёжнее всего — прямой захват трафика: если сегмент, помеченный как «пропавший», в итоге появляется в дампе позже, это reordering, а не потеря. Косвенно на reordering указывает сочетание растущих ретрансмитов с чистой статистикой потерь по mtr на длительном наблюдении, но окончательный ответ даёт только анализ реальной последовательности пакетов.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →