Решая задачу компенсации потерь (связанных с нарезкой канала провайдером и аппаратных потерь на VDSL), я использовал следующую схему. На входе сети стоял роутер с двумя эзернетами, на каждом из которых я повторял скорости провайдера/железа, но с увеличенной задержкой и точностью нарезки, а также оставлял чуть меньше (условно говоря - 99% скорости). Таким образом я помогал алгоритмам Congestion Control выровнять скорость ниток TCP и трафик в целом до того, как пакеты начнут прибиваться. Например: провайдер предоставляет 10Мбит физические и внутри вырезан канал 1Мбит Х 1Мбит, я вырезаю 99% от 1Мбит на эзернете, смотрящем в LAN (часто и наружу, но по несколько другой причине - компенсация разности в точности/неровностях таймеров).

Второй способ выравнивания состоит в вырезании в уже упомянутом 1Мбит подканала (поначалу я использовал около 20%) под ACK и некоторые другие пакеты. Таким образом я использовал интерактивные свойства TCP для грубой подстройки входящего трафика, так как каждый управляющий пакет влечет за собой некий трафик в обратную сторону. В дальнейшем я стал использовать простейший демон на Perl, который каждый квант (100-500мсек) проверяет статистику (от rate estimators) и устанавливает реальный rate данного ACK-канала в зависимости от соотношения estimated rates. Пусть R1 - ширина канала (1Мбит в примере), r2 - ширина обратного ACK-канала, E1 - estimated rate канала, e2 - estimated ACK rate. Допустим, отношение ACK трафика к основному примерно константа (по крайней мере - относительно медленно меняется), т.е. в среднем ACK трафик порождает примерно фиксированный обратный трафик. Тогда:
r2 / R1 = e2 / E1
r2 = ( e2 * R1 ) / E1
(в реале я использовал какие-то еще коррекции, которые сейчас не помню, есть в Perl-коде, например - экстраполяцию на малом числе точек для прогнозирования трафика E1)

Однако все стандартные шедулеры (CBQ, HTB...) имеют ряд недостатков. Главный общий недостаток - использование реальных таймеров для определения задержки пакетов. В Linux эти таймеры даже можно выбирать при конфигурировании ядра перед компиляцией - в зависимости от скорости, точности и других опций конфигурации ядра. Но в любом случае скорости трафика выше чем точность таймера, или частота таймера (Timer frequency) должна быть очень высокой. В некоторых патчах ядра реализованы очень высокие частоты, но чем выше частота - тем ниже скорость работы системы, в пределе система только тем и занимается, что считает время. Также ухудшают характеристики некоторых таймеров SMP системы (что может свести на нет многоядерность/многопроцессорность системы - ее поддержку на роутере может даже придется выключить).

Однако, как выяснилось, не все так плохо. В Японии в рамках программы распределенных GRID вычислений для быстрых длинных каналов (long-distance fat networks) и проекта GridTCP создан шедулер QoS "PSPacer" (или "Precise Software Pacer" или "PSP"), который вообще может не использовать системый таймер для идеального распределения трафика. Расстояние между пакетами заполняется пакетами извне данной полосы и PAUSE-пакетами (вырожденными IEEE 802.3x PAUSE packets), то есть использует только таймер сетевой карты и/или концентратора (смотря кто терминирует PAUSE). Это требует некоторой внимательности к оборудованию - частичной поддержки сетевыми картами и/или концентраторами 802.3x стандарта (full-duplex ethernet flowcontrol, в основном начал использоваться начиная с 100Мбит, полная поддержка - в 1Гбит оборудовании, но реально все работает даже на картах rtl8139, но терминируется на свиче). Причем поддержку flowcontrol на концентраторах обычно надо наоборот выключать, что иногда может усложниться проблемами с доступом к чужому оборудованию. Таким образом единственное требование, которое остается к роутеру - достаточная мощность для пропускания данного потока (или доделать измерение реальной скорости маршрутизации). PSPacer не использует удаление пакетов, а только задерживает их, реализуя lostless алгоритм (реально даже памяти обычного компьютера хватает для >200мсек задержки на >1Гбит). Однако листовые шедулеры могут и удалять пакеты (я иногда использую SFQ).

Однако японцы решают несколько иную проблему. Им необходимо утилизировать очень длинный оптический канал, например, 10Гбит, с неравномерными потерями. Для этого используются GRID-технологии - полузакрытый (рассылается после подписания документов) демон обменивается между роутерами на концах канала по протоколу MPI информацией о трафике и корректирует точную скорость канала на PSPacer. В остальных отношениях шедулер сырой и и некоторые вещи (например, иерархия скоростей более одного уровня) просто не работают, хоть и успешно конфигурируются. Для наших же целей шедулер придется серьезно исправить (что я вроде даже сделал - в рамках вышеописанной функциональности).

Далее идут малопроверенные, но работающие у меня уже почти полгода соображения.

Используя подсчет повторов (retransmissions) TCP пакетов можно (теоретически) проконтролировать реальную ширину даже исходящего канала без информации от удаленного роутера (провайдера). Эту задачу можно попытаться решить через исправленный netfilter/conntrack, однако этот метод чувствителен к ширине канала и атакам, поэтому я предпочел свой код с нечетким трекингом.

Самый странный момент. Я динамически (по мере создания/удаления контрольных TCP-треков) строю двоичное дерево IP-адресов и пересчитываю статистику по трафику/повторам вниз от листьев данного дерева к корню для каждого бита адреса. Теоретически, данная статистика примерно когерентна характеристике реальных сетей и может отражать не только (и не столько) "здоровье" ближайшего канала, сколько активных удаленных сетей. Но так как я (пока?) использую заведома не очень правильный (математически по-моему совсем неправильный, но удобный в реализации) метод статистики, я использую полученную "задержку" не для реальной задержки пакета, а лишь для сортировки пакетов в очереди, т.е. пакет только может быть задержан приоритетно. Хотя код для точной задержки (individual per-packet gap) уже работоспособен и может быть включен при уверенности в точности задержки. Пока же статистика вычисляется как два средних арифметических - между трафиком для данного IP/бита и - отдельно - потерями (повторами) для него же. Другие методы сопряжены со сложностями с плавающей точкой в ядре или неконтролируемым ростом числителя и знаменателя дроби (на мой взгляд - если считать коэффициент повторов).

Также я написал код для специфического rate estimator для данного шедулера, включая замену Perl-демона, но даже ни разу его не тестировал, а пока пользуюсь старым кодом. Скорей всего данный estimator пока неработоспособен и нуждается в серьезной отладке. Первая цель данного кода - определение скорости маршрутизации (для сверхскоростных каналов и реального компьютера), так как эта проблема неоднократно упоминалась в переписке Takano Ryousei, однако проблема слишком быстрой сети мне не встретилась даже на относительно старом медленном компе и для тестирования необходимо искуственное моделирование ситуации.


News:

©Денис Каганович