на сей раз речь пойдет о шейперах. это методы ограничения «ширины» канала. для чего это нужно. например у вас один абонент подключён с помощью лини связи 1 Гбит/с. но давать такую полосу пропускания одному абоненту довольно опасно.
что если абонент полностью займёт эту полосу? какой итоговый канал связи необходим в этом случае для всей сети? что будет с трафиком остальных абонентов?
для решения этих задач применяют два основных метода.
- ограничение скорости т.е. полосы пропускания которую может занять один абонент
- второе это иерархия приоритетов трафика.
в статье будут в основном рассмотрены алгоритмы первого пункта. стоит сразу отметить что это касается главным TCP т.к. ТСP имет механизмы контроля передачи. сделать что-то с «бестолковым» UDP сложнее. применяют разные методы, но здесь я их рассматривать не буду.
собственно в чем проблема? ну например передача голоса уязвима к потери пакетов, но занимает сравнительно небольшую полосу пропускания. сюда же можно отнести динамичные онлайн игры. а например тот или иной протокол передачи файлов мало чувствителен к потерям пакетов, но может занять весь канал. а пользователю в большинстве случаев не важно скачался файл за 5 секунд или за 7,5.
самый логичный способ организации шейпера следующий. шейпер имитирует линию связи, передавая пакеты с такой скоростью, чтобы средняя скорость за промежуток времени не превышала заданный лимит.
но ведь пакеты до шейпера передаются с большей скоростью как быть с ними? решение довольно простое: давайте и складировать их в буфер. в ввиду того, что время между подтверждением принятых пакетов растягивается, сервер будет снижать скорость передачи пакетов идущих на абонентов.
способ хорош тем что он очень деликатный. не заставляет сервер заниматься повторной передачей пакетов. при достаточно большом размере буфера не один пакет не потеряется.
но способ имет ряд принципиальных недостатков. во-первых, задержку испытывают все пакеты, даже те, которые передаются в рамках разрешённой полосы. т.е. задержку испытывают все пакеты т.к. попадают в одну очередь.
а хотелось бы передавать трафик в рамках разрешённой полосы максимально быстро. это важно геймерам, для видео или голосовой связи.
второй недостаток такого способа — ему нужно достаточно большое количество ресурсов, что неудобно на устройстве имеющим очень ограниченные ресурсы — например на коммутаторе.
но в чем суть способа? растягивание времени между подтверждениями о приёме пакета.
тогда можно поступить так. не пытаться хранить пакет, а использовать сравнительно короткие очереди. все что в очередь не влезло просто отбросить — Tail Drop.
тогда подтверждения о том что пакет прият, сервер так и не дождётся, что заставляет сервер выжидать тайм аут до повторной передачи пакета. и только выждав это время сервер предпримет попытку повторной отправки пакета.
алгоритм Tail Drop можно эффективно комбинировать с алгоритмами полисинга, например, реализующими концепцию "раскраски" трафика в разные цвета.
- зелёный когда алгоритм в трафик не вмешивается и данные передаются с минимальной задержкой.
- жёлтый — это когда трафик на грани вмешательства. например кратковременное превышение полосы пропускания.
- красный — полоса превышена на длительное время — алгоритм начинает жёстко отбрасывать пакеты.
ну что же вроде бы все хорошо: можно передавать трафик с минимальной задержкой например для динамичных игр, а скачивание фалов немного урезонить.
но представьте себе такую ситуацию. алгоритм работает на соединении с вышестоящим провайдером. тогда будет эффект синхронизации TCP серверов. когда они что-то передают и вдруг и всем одновременно говорят «стоп, стоять не так быстро!». т.е. серверы начинают синхронно передачу и также синхронно её прекращают. это создаёт волнообразные нагрузки на сеть, что не очень хорошо.
проблему решает RED (Random Early Detection). смысл в том чтобы не ждать заполнения очереди.
задаются параметры отбрасывания пакетов. при размере очереди. очередь < min — пакеты не отбрасываются. размер очереди > max — все пакеты отбрасываются (как в Tail Drop). но зоне между порогами — пакеты отбрасываются с некоторой вероятностью, которая растёт по мере заполнения очереди.
да это ломает синхронизацию TCP. разные соединения теряют пакеты в разное время, что приводит к более плавному и равномерному использованию канала.
основной недостаток: сложность настройки. нужно точно подобрать пороги и параметры под конкретную пропускную способность и нагрузку. на разных каналах настройки будут разными. на практике часто работает нестабильно.
CoDel (Controlled Delay): вычисление частоты отбрасывания пакетов.
идея отказаться от управления на основе размера очереди (сколько пакетов ждут) и перейти к управлению на основе задержки (как долго пакеты ждут).
обычно задаются параметры: target максимальное время нахождения пакета в очереди, interval промежуток времени, за которое алгоритм пытается исправить ситуацию.
алгоритм постоянно измеряет время пребывания пакетов в очереди (sojourn time). если время превышает target дольше, чем на протяжении interval, CoDel решает, что очередь переполнена. в этом случае отбрасывается один пакет (обычно самый старый в очереди — голова очереди). это самый эффективный сигнал для TCP.
но ситуация может не улучшится, тогда частота отбрасывания пакетов экспоненциально увеличивается (вычисляется новая частота).
восстановление: как только задержка падает ниже target, сброс пакетов мгновенно прекращается.
вроде бы все хорошо. что можно еще улучшить? ну наверное улучшить работу на аплиньке провайдера. чтобы максимально равномерно разделять полосу между абонентами, не позволяет одному "плохому" потоку (торрент) заставлять страдать все остальные (звонок по Zoom).
FQ-CoDel (Fair Queuing + CoDel): добавляет к CoDel механизм справедливого разделения канала (Fair Queuing). трафик делится на множество независимых потоков, к каждому потоку применяется свой экземпляр CoDel.
решение выглядит практически идеальным. но как известно идеальных решений не бывает. все таки решение требует значительных ресурсов — например нужно как-то идентифицировать поток. для этой цели обычно используют некий хеш, который предварительно нужно ещё вычислить.
поэтому применение более простых алгоритмов (например, на коммутаторах доступа) может быть оправдано и дать приемлемый эффект. хотя конечно лучше иметь возможность еще немного зажать общий трафик сети для более равномерной на неё нагрузки и изолировать потоки трафика друг от друга для справедливого разделения полосы пропускания между абонентами.
кроме того современные тренды таковы что борьба идёт не за утилизацию 100% канала любой ценой, а за предсказуемую и низкую задержку (low latency). такие алгоритмы, как FQ-CoDel, — это отличный инструмент для достижения цели.
текст создан при экспертной поддержке DeepSeek