Tcp congestion control что это

Обновлено: 07.07.2024

Бывало ли у вас такое, что ставите файл на закачку, и скорость медленно, но верно возрастает, затем, в какой-то момент, резко снижается, затем опять возрастает? Закачка файла в один поток не обеспечивает полную скорость канала? Запускаете торрент-клиент, и пинг в игре сильно прыгает? Используете 3G-модем (или другую линию с относительно большой потерей пакетов) и не можете это терпеть?
Наверняка вы винили во всем ваш роутер, либо обвиняли своего провайдера в кривой настройке шейпера? Это влияет, но виноваты не они.
Итак, встречайте:

TCP Congestion Control, или TCP Congestion Avoidance Algorithm.
Что это такое?

Вкратце ­— алгоритмы, которые пытается сделать все возможное, чтобы обеспечить наиболее быструю скорость передачи данных между двумя узлами, передающими данные через TCP. Они управляют размером TCP-окна и могут ориентироваться на RTT (Round Trip Time — время от отправки запроса до получения ответа), потерю пакетов, время ожидания отправки пакета из очереди и т.д. Каждый алгоритм по разному ведет себя в той или иной ситуации и нет какого-то универсального.

Долгое время, в ходу были алгоритмы Reno, разработанный в 1990 году, и BIC. Первый применялся во всех ОС Windows до XP, а второй — в Linux до 2.6.18. Затем, в Windows Vista появился новый алгоритм Compound TCP, а в Linux сменили BIC на Cubic.

Какие есть алгоритмы?
  • BIC TCP
  • CUBIC TCP
  • Highspeed TCP
  • H-TCP
  • TCP Hybla
  • TCP-Illinois
  • TCP Low Priority
  • TCP Vegas
  • TCP NewReno
  • TCP Veno
  • TCP Westwood+
  • YeAH-TCP
Тест 3G

image

К сожалению, CUBIC, который используется по умолчанию во всех дистрибутивах, совершенно не подходит, например, для 3G-соединений. Ниже представлен график сравнения 4 алгоритмов congestion avoidance для HSDPA сетей за конец 2012 года из TCP Congestion Control over HSDPA: an Experimental Evaluation:

Как видите, CUBIC в отстающих. Он значительно повысил RTT на полной утилизации 3G канала, в то время как Westwood+ и NewReno более-менее справляются с этой проблемой.
Давайте взглянем на количество ретрансмиссий:

image

Как видно из графика, у CUBIC относительно большое количество ретрансмиссий

image

В то же время, он лидирует в скорости передачи данных за единицу времени.

Что это значит? Это значит, что с использованием Westwood+ или NewReno вы сможете комфортней серфить интернет, пока у вас скачивается большой файл.

Тест WiMAX и WiFi каналов

Тест взят из Comparative Performance Evaluation of TCP Variants in WiMAX (and WLANs) Network Configurations — еще одного интересного сравнения алгоритмов для беспроводных сетей.

image

В тесте №1 используется соединение компьютер-wimax.роутер-wimax.клиент с пропускной способностью между компьютером и роутером в 100 мбит/с и RTT в 45 мс и соотношением DL:UL 1:1 между wimax роутером и клиентом.
Зависимость эффективной передачи данных от потери пакетов:

Чуть изменим тест. В тесте №2 используется схема компьютер-роутер1-роутер2-wimax.роутер-wimax.клиент, где RTT 10 мс. между компьютером и первым роутером, далее используется 10 мбит/с канал с 25 мс. RTT, между вторым и wimax роутером канал опять 100 мбит/с c RTT в 10 мс.

image

image

Как видно из графиков, лидерство держит Westwood.
Картина для WiFi схожа с WiMAX:

image

Тест высокоскоростного канала

Этот тест взят из технического отчета алгоритма YeAH-TCP за 2006 год. Теоретически, YeAH является самым продвинутым алгоритмом и нацелен работать как можно лучше на высокоскоростных линиях, на линиях с высокой задержкой или высокими потерями пакетов.
Тесты делались с импользованием канала пропускной способностью в 500 mbit/s

image

В эффективной передаче данных в зависимости от RTT лидирует YeAH

image

Зависимость эффективной передачи данных и потери пакетов, опять YeAH занимает первое место

К сожалению, с YeAH на ядре 3.7 какие-то проблемы, через некоторое время он весит систему software interrupt'ами. Такого поведения не наблюдается на 3.6.

Как поменять?

Сменить Congestion Algorithm достаточно просто, всего одна строка:
sysctl -w net.ipv4.tcp_congestion_control=westwood
Где вместо westwood можно вставить названия из /lib/modules/. /kernel/net/ipv4/tcp_. ko без префикса tcp_.

Вместо заключения

На каналах вроде домашнего вайфая, рекомендую использовать Westwood или H-TCP. Для проводных каналов хорошим выбором может быть YeAH (если у вас не наблюдается с ним проблем), H-TCP или Illinois.

AIMD

В TCP для определения размера окна перегрузки используется метод аддитивного увеличения, мультипликативного уменьшения. Суть метода заключается в том, что при получении каждого подтверждения, мы прибавляем к размеру окна некоторые значения, как правило это размер одного сегмента TCP, а если перегрузка произошла, то мы умножаем размер окна на некоторые значения. Как правило это 1/2, то есть в TCP при перегрузке, размер окна уменьшается в два раза.

p, blockquote 15,0,0,0,0 -->

Формула для определения размера окна в TCP

Размер окна AIMD

Вот график работы метода аддитивного увеличения, мультипликативного уменьшения. Мы начинаем передавать данные, поступает подтверждение,размер окна увеличивается, происходит аддитивное увеличение. Затем в сети произошла перегрузка, размер окна уменьшается в два раза, произошло мультипликативное уменьшение.

p, blockquote 17,0,0,0,0 -->

График работы метода аддитивного

p, blockquote 18,0,0,0,0 -->

Затем данные снова передаются, размера окна при получение каждого подтверждения увеличивается на один сегмент, аддитивные увеличения. И так происходит пока не произойдет следующая перегрузка. Таким образом, размер окна у нас напоминают зубья пилы.

p, blockquote 19,0,0,0,0 -->

Как сделать быстро

Все наши сервера управляются с помощью протоколов удаленного управления, в данном случае SSH. Если вы еще не сталкивались с протоколом SSH, то можете попросить вашего системного администратора настроить ваш сервер. Я расскажу, как его убедить это сделать.


Что же такое BBR? Это один из алгоритмов, который позволяет нам управлять тем, как пакеты уходят в сеть. И конфигурируется он следующими двумя параметрами. Первое — выставление планировщика пакетов в FQ, далее я расскажу, почему стоит использовать FQ. Второе — включение самого congestion control, то есть самого BBR.

Кажется, что на этом мы могли бы закончить. Но на самом деле существует много подводных камней, и, скорее всего, ваш системный администратор просто так не включит BBR на сервере. Поэтому мы пойдем дальше.


Начнем мы с наших браузеров, с которыми мы каждый день взаимодействуем. Мы видим консоль веб-разработчика. В консоли есть интересное для нас поле — protocol.



Мы имеем первую часть в виде заголовков, которая отделена символами “\r\n\r\n” от нашего тела. Мы здесь просто запрашиваем обычный ресурс методом GET, поэтому тела у этого запроса не будет. И мы видим, что байтики приблизительно похожи на то, что есть в ASCII-таблице. Мы запрашиваем какую-то JS-ку, какой-то ресурс. Также есть заголовок Host, указывающий домен, с которым мы сейчас работаем. И — какой-то дополнительный набор заголовков. Они могут быть кастомными, можно использовать любые.



Также мы можем наблюдать в первой строчке, что в один пакет могут поместиться сразу два фрейма. Мы не будем подробно останавливаться на том, какие фреймы существуют, их достаточно много. В данном случае нам будет интересен фрейм headers, потому как раз позволяет запрашивать ресурсы. Я немного участвовал в разработке Wireshark, помогал его улучшать в этом поле.

Мы видим, что есть get-запрос. Видим, что в середине есть текстовое представление этого get-запроса. Но в правой колонке видим только один выделенный байт, и он как раз будет являться этим методом get. Далее я расскажу, почему так происходит.

Дальше у нас есть заголовок path, который указывает путь до ресурса, до нашей JS-ки, которую мы будем запрашивать. И есть набор каких-то дополнительных заголовков, которые также будут присутствовать в нашем запросе.

Так почему же наши байты в сети не совпадают с тем, как это все рисуется на нашей картинке? Дело в том, что Wireshark показывает нам конечный результат, как он это все расшифровал. А в сети эти байты, эти заголовки, будут сжаты специальным форматом HPACK. Более подробно лучше ознакомиться в интернете. Поищите информацию, она хорошо документирована.

Вернемся к нашим байтикам. Есть специальное поле — идентификатор контента. Оно указывает, с каким ресурсом данные фреймы работают в текущий момент. В данном случае первый фрейм мы отправили, получили данные. Когда сервер будет нам отдавать сами байтики содержимого, уже будет использоваться фрейм data.



Клиент и сервер как бы устанавливают надежное соединение, TCP-соединение. Мы отправляем от клиента два пакета серверу, и один пакет отправляется со стороны сервера клиенту. Тем самым мы как бы говорим, что готовы передавать данные. Это, конечно, создает накладные ресурсы, мы это можем делать достаточно долго.


Также существует шифрование. Если вы сейчас посмотрите на ваш браузер, скорее всего, увидите иконочку с замочком. Многие ее называют SSL, но на самом деле это не SSL. Это TLS. SSL уже давно устарел, уже практически не поддерживается, и стоит от него отказаться.

У TLS тоже есть обмен пакетов. То есть мы так же, как и в случае TCP-хендшейка, устанавливаем определенное состояние, после которого можем продолжать работать. В этом месте мы тоже можем заниматься оптимизацией, но браузеры пока не поддерживают те вещи, которые мы уже включили со стороны сервера. Будем ожидать, когда все это включат.



Каждое наше соединение из шести, которые мы уже создали, на самом деле, не будет закрываться. То есть они продолжат работать так же, как и раньше. Для этого используется техника типа Keep-Alive. То есть мы создаем надежное соединение до конкретного сервера и продолжаем работать.


Наблюдается потеря пакетов. Они тормозят нашу загрузку, и такая блокировка называется Head-of-line blocking.

Коллапс перезагрузки

p, blockquote 8,0,0,0,0 -->

p, blockquote 9,0,0,0,0 -->

TCP BBR: быстрый и простой способ ускорения загрузки страниц. Доклад Яндекса

В последние восемь лет я занимаюсь больше бэкенд-разработкой, и, скорее всего, мои знания остались на уровне AngularJS первых версий. Вы, скорее всего, лучше меня знаете, как это все работает. Вы всё уже оптимизировали, всё сжали, и здесь я вам ничего посоветовать не смогу.


Но я могу вам посоветовать, как ускорить вашу сеть с помощью оптимизации самого сервера, самой операционной системы.

Чтобы что-то ускорить, нужны метрики. В данном случае мы использовали следующие: среднее время до первого байта показывает нам, как быстро работает уровень TCP, а вторая метрика — время получения HTML от первого байта. Мы произвели эксперименты, замерили наши метрики, и после включения BBR наше ускорение составило приблизительно десять процентов.


Чтобы понимать, что такое десять процентов, мы обратимся к абсолютному значению, которое составляет 66 миллисекунд. Если вы зайдете в свою любимую многопользовательскую онлайн-игру, то Ping до западноевропейских серверов будет составлять приблизительно 60-70 миллисекунд.

Медленный старт

p, blockquote 22,0,0,0,0 -->

При медленном старте размер окна увеличивается на каждое подтверждение не на 1 сегмент, а на 2, благодаря этому происходит экспоненциальное увеличение размера окна. Сначала мы отправляем один сегмент, получили подтверждение, отправляем два сегмента, получили 2 подтверждения, на каждое подтверждение отправляем по два сегмента всего 4, потом 8, потом 16 и так далее. То есть несмотря на название медленный старт, размер окна увеличивается гораздо быстрее, чем при аддитивном увеличении, мультипликативном уменьшении.

p, blockquote 23,0,0,0,0 -->

Недостаток метода заключается в том, что если произошла потеря сегмента, то размер окна уменьшается до нуля. Таким образом медленный старт быстро разгоняется, но также быстро тормозится.

p, blockquote 24,0,0,0,0 -->

Медленный старт и AIMD в TCP

В TCP используется комбинация медленного старта и аддитивного увеличения мультипликативного уменьшения. Cначала используется медленный старт, для того, чтобы быстро заполнить доступную пропускную способность канала. После того, как размер окна достиг определенного значения, порог медленного старта, происходит переход на аддитивное увеличение мультипликативное уменьшение. И дальше уже используется этот метод, размер окна увеличивается медленно, но если пришел сигнал о перегрузке, размер окна уменьшается в два раза, а не снижается до нуля.

p, blockquote 25,0,0,0,0 -->

График работы метода аддитивного

p, blockquote 26,1,0,0,0 -->

Порог медленного старта определяется следующим образом. Сначала запускается медленный старт и работает до того, пока не поступит сигнал о перегрузке, после этого размера окна при котором пришел сигнал о перегрузке делится в два раза, и это значение выбирается порогом медленно старта. Таким образом, формируется достаточный запас размера окна, для того чтобы не произошла перегрузка, которые при аддитивном увеличении скорее всего не будет достигнут, если конечно загрузка сети останется на том же самом уровне.

p, blockquote 27,0,0,0,0 -->

p, blockquote 28,0,0,0,0 -->

Если использовать потерю сегментов в качестве сигнала и перегрузки, то TCP фактически будет работать в режиме, который ведёт к перегрузке. TCP постоянно увеличивает размер окна пока перегрузка не произойдет, причем о перегрузке TCP узнает только после того, как она произошла, поэтому предотвратить ее при такой схеме нельзя.

p, blockquote 29,0,0,0,0 -->

Другая проблема называется глобальной синхронизацией TCP и TCP global synchronization, она заключается в том, что когда на маршрутизаторе на котором произошла перегрузка заканчивается место в буфере, он отбрасывает сегменты всех отправителей.

p, blockquote 30,0,0,0,0 -->

Отправитель обнаруживает потерю сегмента, понимает что произошла перегрузка уменьшает размер окна. В TCP в отличии от Ethernet или wi-fi, не встроена схема рандомизированное задержки, поэтому все отправители после уменьшения размера окна начинают передавать данные примерно в одно и то же время. В результате на маршрутизатор опять приходит большое количество пакетов, что в свою очередь ведет к перегрузке. Для того чтобы решить эти проблемы используются другие сигналы о перегрузке, которые мы сейчас рассмотрим.

p, blockquote 31,0,0,0,0 -->

Управление скоростью передачи в TCP

p, blockquote 13,0,1,0,0 -->

С другой стороны, если мы будем отправлять в сеть большое количество сегментов, то сеть может оказаться перегруженной, маршрутизаторы начнут отбрасывать наши сегменты, их нужно будет отправлять заново и скорость передачи данных опять окажется низкой. Таким образом нам нужно определить оптимальный размер окна, для того чтобы мы могли передавать данные по сети избегая загрузки, и приложение могло принять эти данные, и записать их в свой буфер.

p, blockquote 14,0,0,0,0 -->

Задержка сегмента

Один из возможных вариантов, задержка сегмента. В этом случае измеряется round trip time (RTT) время движения сегмента от отправителя до получателя и обратно.

p, blockquote 32,0,0,0,0 -->

Задержка сегмента в TCP

p, blockquote 33,0,0,0,0 -->

Отправитель передавая сегменты, засекает RTT, измеряет средние время, и при существенном увеличении RTT уменьшается размер окна перегрузки.

p, blockquote 34,0,0,0,0 -->

Задержка сегмента в TCP

p, blockquote 35,0,0,0,0 -->

Измерение времени задержки сегмента, позволяет обнаружить перегрузку до того как она произошла, но задержка может быть вызвана не только перегрузкой, но и другими причинами. Поэтому задержка сегмента не такой надежный сигнал, как его потеря.

p, blockquote 36,0,0,0,0 -->

Другая проблема заключается в том, что когда мы используем такой сигнал о перегрузке на загруженных каналах связи, то это приводит к не справедливому распределению пропускной способности. Наш компьютер начинает уменьшать размер окна перегрузки, когда увеличивается время задержки сегмента, в то же время другие компьютеры, которые используют сигнал о перегрузке потери сегмента, продолжают увеличивать размер окна пока перегрузка не произойдет.

p, blockquote 37,0,0,0,0 -->

Решением является совместное использование двух сигналов задержки и потери сегмента, такой подход используется например, в протоколе Compound TCP реализованного компанией Microsoft.

p, blockquote 38,0,0,0,0 -->

Сигнал о перезагрузке

Как отправитель узнает, о том что в сети произошла перегрузка? Это достаточно сложная задача, потому что сеть может быть составной, и перегрузка может происходить не на том сегменте сети, который подключен к отправителю, а на каком-то сегменте между отправителем и получателем, который находятся достаточно далеко от того и другого.

p, blockquote 20,0,0,0,0 -->

Чаще всего на практике, в качестве сигнала перегрузки используется потеря сегмента. Считается, что сейчас каналы связи уже хорошего качества и если произошла потеря сегмента, то не из-за ошибки канала, а из-за того, что сеть перегружена, поэтому нужно уменьшить размер окна, для того чтобы избежать дальнейшей перегрузки.

p, blockquote 21,0,0,0,0 -->

Как все это применить?

Я намеренно для изменения параметров использовал утилиту sysctl, а значения этих параметров просто читал с помощью cat. Сделано это было все для того, чтобы вам было ясно что способов изменения параметров ядра много.

Например, внести изменения мы так же можем с помощью echo:

А прочитать значения мы так же можем с помощью команды sysctl:

Настройка ядра Linux для тяжелых проектов и защиты от DDOS

Настройка ядра Linux для тяжелых проектов и защиты от DDOS

Сейчас речь пойдет о том, как можно настроить ядро Linux сервера, чтобы он смог переваривать большое количество запросов, а так же DDos. Все будет кратко с небольшим описанием.

Много материалов я вычитал, для того чтобы понять какие значения оптимальны, так же прилично всего перепробовал, и некоторые решения помогли мне уйти от той высокой нагрузки на сервере. Так же грамотная настройка ядра сервера позволит вам защититься от SYN flood. Ладно, начнем..

rp_filter

По умолчанию он отключен:

Так проверку можно включить на определенном интерфейсе:

accept_source_route

По умолчанию эта опция отключена:

accept_redirects, secure_redirects, send_redirects

По умолчанию все эти параметры включены:

Но, так как наш сервер не маршрутизатор, в них нет необходимости:

icmp_echo_ignore_broadcasts

По умолчанию включено, т.е. broadcast icmp запросы приходить не будут:

Так и рекомендуется отставить:

icmp_ignore_bogus_error_responses

Так и рекомендуется отставить:

icmp_echo_ignore_all

На ваше усмотрение, можно отключить:

Как проверить, включен ли он у нас:

Таким образом, как описано выше, мы получаем неплохую защиту от syn флуда и терпим небольшую нагрузку на ЦП..

tcp_max_syn_backlog

Если на сервере возникают перегрузки, можно попытаться увеличить это значение, например до 4096:

tcp_synack_retries

Целочисленное значение (1 байт) tcp_synack_retries определяет число попыток повтора передачи пакетов SYNACK для пассивных соединений TCP. Число попыток не должно превышать 255. Значение 5 соответствует приблизительно 180 секундам на выполнение попыток организации соединения.

Это значение имеет смысл уменьшить, например до 1 (это будет 9 секунд):

tcp_max_orphans

Целочисленное значение параметра tcp_max_orphans определяет максимальное число допустимых в системе сокетов TCP, не связанных каким-либо идентификатором пользовательского файла (user file handle). При достижении порогового значения “осиротевшие” (orphan) соединения незамедлительно сбрасываются с выдачей предупреждения. Этот порог помогает предотвращать только простые атаки DoS. Не следует уменьшать пороговое значение (скорее увеличить его в соответствии с требованиями системы – например, после добавления памяти. Каждое orphan-соединение поглощает около 64 Кбайт не сбрасываемой на диск (unswappable) памяти.

Рекомендуется установить 65536, а далее увеличивать, по мере необходимости:

tcp_fin_timeout

Целое число в файле tcp_fin_timeout определяет время сохранения сокета в состоянии FIN-WAIT-2 после его закрытия локальной стороной. Партнер может не закрыть это соединение никогда, поэтому следует закрыть его по своей инициативе по истечении тайм-аута. По умолчанию тайм-аут составляет 60 секунд. В ядрах серии 2.2 обычно использовалось значение 180 секунд и вы можете сохранить это значение, но не следует забывать, что на загруженных WEB-серверах вы рискуете израсходовать много памяти на сохранение полуразорванных мертвых соединений. Сокеты в состоянии FIN-WAIT-2 менее опасны, нежели FIN-WAIT-1, поскольку поглощают не более 1,5 Кбайт памяти, но они могут существовать дольше.

Рекомендуется поменять на 10 секунд:

tcp_keepalive_time

Переменная определяет как часто следует проверять соединение, если оно давно не используется. Значение переменной имеет смысл только для тех сокетов, которые были созданы с флагом SO_KEEPALIVE.

По умолчанию 2 часа:

Рекомендуется каждую минуту:

tcp_keepalive_intvl

Целочисленная переменная tcp_keepalive_intvl определяет интервал передачи проб. Произведение tcp_keepalive_probes * tcp_keepalive_intvl определяет время, по истечении которого соединение будет разорвано при отсутствии откликов. По умолчанию установлен интервал 75 секунд, т.е., время разрыва соединения при отсутствии откликов составит приблизительно 11 минут.

tcp_keepalive_probes

Целочисленная переменная tcp_keepalive_probes задает число передач проб keepalive, после которого соединение считается разорванным. По умолчанию передается 9 проб.

netdev_max_backlog

Рекомендуется так и оставить:

somaxconn

Рекомендуется установить значения в районе 15000-20000:

tcp_mem

Векторная (минимум, режим нагрузки, максимум) переменная в файле tcp_mem cодержит общие настройки потребления памяти для протокола TCP. Эта переменная измеряется в страницах (обычно 4Кб), а не байтах.
Минимум: пока общий размер памяти для структур протокола TCP менее этого количества страниц, операционная система ничего не делает.
Режим нагрузки: как только количество страниц памяти, выделенное для работы протокола TCP, достигает этого значения, активируется режим работы под нагрузкой, при котором операционная система старается ограничивать выделение памяти. Этот режим сохраняется до тех пор, пока потребление памяти опять не достигнет минимального уровня.
Максимум: максимальное количество страниц памяти, разрешенное для всех TCP сокетов. В этой переменной задаются 3 значения, определяющие объем памяти, который может быть использован стеком TCP. Значения измеряются в страницах памяти. Размер одной страницы зависит от аппаратуры и конфигурации ядра. Для архитектуры i386 размер одной страницы составляет 4Кб, или 4096 байт. Некоторые, более новые аппаратные реализации, имеют размер страницы равный 16, 32 или даже 64 Кб. Все три значения по-умолчанию рассчитываются во время загрузки. Первое число задает нижний порог. Ниже этого порога, стек TCP вообще никак не беспокоится об управлении памятью, используемой различными TCP сокетами. Когда объем используемой памяти достигает второго предела (числа), то TCP начинает более энергично расталкивать память, стремясь освободить ее как можно быстрее. Этот процесс продолжается до тех пор, пока объем использумой памяти не достигнет нижнего предела. И последнее число максимальный объем памяти, который может использоваться для нужд TCP. Если используемый объем памяти достигнет этого порога, то TCP просто начинает терятьпакеты и соединения до тех пор, пока объем используемой памяти не уменьшится. Эта переменная может позволить несколько увеличить пропускную способность на толстых каналах, если должным образом настроить переменные tcp_mem, tcp_rmem и tcp_wmem. Впрочем, переменная tcp_rmem не требует особо пристального внимания, поскольку серия ядер 2.4 имеет достаточно хорошие настройки этой переменний, а вот на другие две следует взглянуть поближе. Дополнительную информацию об этом вы найдете в руководстве TCP Tuning Guide.

Можно поставить эти же значения.увеличивать имеет смысл в случае увеличения нагрузки.

tcp_rmem

Векторная (минимум, по умолчанию, максимум) переменная в файле tcp_rmem содержит 3 целых числа, определяющих размер приемного буфера сокетов TCP.
Минимум: каждый сокет TCP имеет право использовать эту память по факту своего создания. Возможность использования такого буфера гарантируется даже при достижении порога ограничения (moderate memory pressure). Размер минимального буфера по умолчанию составляет 8 Кбайт (8192).
Значение по умолчанию: количество памяти, допустимое для буфера передачи сокета TCP по умолчанию. Это значение применяется взамен параметра /proc/sys/net/core/rmem_default, используемого другими протоколами. Значение используемого по умолчанию буфера обычно (по умолчанию) составляет 87830 байт. Это определяет размер окна 65535 с заданным по умолчанию значением tcp_adv_win_scale и tcp_app_win = 0, несколько меньший, нежели определяет принятое по умолчанию значение tcp_app_win.
Максимум: максимальный размер буфера, который может быть автоматически выделен для приема сокету TCP. Это значение не отменяет максимума, заданного в файле /proc/sys/net/core/rmem_max. При «статическом» выделении памяти с помощью SO_RCVBUF этот параметр не имеет значения.

Можно поставить эти же значения.увеличивать имеет смысл в случае увеличения нагрузки. В одном из источнике рекомендовали следующие значения:

tcp_wmem

Векторная переменная в файле tcp_wmem содержит 3 целочисленных значения, определяющих минимальное, принятое по умолчанию и максимальное количество памяти, резервируемой для буферов передачи сокета TCP.
Минимум: каждый сокет TCP имеет право использовать эту память по факту своего создания. Размер минимального буфера по умолчанию составляет 4 Кбайт (4096)
Значение по умолчанию: количество памяти, допустимое для буфера передачи сокета TCP по умолчанию. Это значение применяется взамен параметра /proc/sys/net/core/wmem_default, используемого другими протоколами и обычно меньше, чем /proc/sys/net/core/wmem_default. Размер принятого по умолчанию буфера обычно (по умолчанию) составляет 16 Кбайт (16384)
Максимум: максимальное количество памяти, которое может быть автоматически выделено для буфера передачи сокета TCP. Это значение не отменяет максимум, заданный в файле /proc/sys/net/core/wmem_max. При «статическом» выделении памяти с помощью SO_SNDBUF этот параметр не имеет значения.

Можно оставить эти же значения. Увеличивать их имеет смысл в случае увеличения нагрузки. В одном из источнике рекомендовали следующие значения:

rmem_default, wmem_default

Их значения по умолчанию:

Можно оставить эти же значения. Увеличивать их имеет смысл в случае увеличения нагрузки. Например, в одном из источнике рекомендовали следующие значения:

rmem_max, wmem_max

Их значения по умолчанию:

Можно оставить эти же значения. Увеличивать их имеет смысл в случае увеличения нагрузки. Например, в одном из источнике рекомендовали следующие значения:

tcp_orphan_retries

Целочисленной значение tcp_orphan_retries определяет число неудачных попыток, после которого уничтожается соединение TCP, закрытое на локальной стороне. По умолчанию используется значение 7, соответствующее приблизительно периоду от 50 секунд до 16минут в зависимости от RTO.

Рекомендуется уменьшить значение этого параметра, поскольку закрытые соединения могут поглощать достаточно много ресурсов (т.е. оставляем 0):

ip_conntrack_max

При слишком маленьких значениях ядро начинает отвергать входящие подключения с соответствующей записью в системном логе:

tcp_timestamps

По умолчанию метки включены:

Кстати, лучше отставить его включенным, иначе не будет работать опция tcp_tw_reuse.

tcp_sack

По умолчанию опция включена:

Рекомендуется включать эту опцию, если вы имеете неустойчивые соединения. Однако, если вы соединены 1.5-метровым кабелем с другой машиной, то в таком случае, для достижения наивысшей скорости обмена, следует эту опцию отключить:

tcp_congestion_control

Начиная с версии 2.6.13, Linux поддерживает подключаемые алгоритмы управления перегрузкой. Используемый алгоритм управления перегрузки можно задать, используя sysctl переменную net.ipv4.tcp_congestion_control, которая по умолчанию установлена в cubic or reno, в зависимости от версии ядра.
Для получения списка поддерживаемых алгоритмов, выполните: sysctl net.ipv4.tcp_available_congestion_control
Выбор опций контроля за перегрузкой выбирается при сборке ядра. Ниже представлены некоторые из опций, доступных в 2.6.23 ядрах:
* reno: Традиционно используется на большинстве ОС. (default)
* cubic: CUBIC-TCP (Внимание: Есть бага в ядре Linux 2.6.18 Используйте в 2.6.19 или выше!)
* bic:BIC-TCP
* htcp:Hamilton TCP
* vegas:TCP Vegas
* westwood:оптимизирован для сетей с потерями
Для очень длинных и быстрых каналов я предлагаю пробовать cubic или htcp, если использование reno желательно.

Для сервера рекомендуется использовать htcp:

tcp_no_metrics_save

По умолчанию опция ничего не запрещает:

Так как это помогает повысить производительность, рекомендуется включить:

net.ipv4.route.flush

Так как в ядре 3.2 она даже не читается, менять ничего не стал.

ip_local_port_range

Содержит два целых числа, которые определяют диапазон локальных портов, которые используются в клиентских соединениях, т.е. для исходящих соединений, которые связывают нашу систему с некоторым узлом сети, где мы выступаем в качестве клиента. Первое число задает нижнюю границу диапазона, второе верхнюю. Значения по-умолчанию зависят от имеющегося объема ОЗУ. Если установлено более чем 128 Мб, то нижняя граница будет 32768, а верхняя 61000. При меньшем объеме ОЗУ нижняя граница будет 1024 а верхняя 4999 или даже меньше. Этот диапазон определяет количество активных соединений, которые могут быть запущены одновременно, с другой системой, которая не поддерживает TCP-расширение timestamp. Диапазона 1024-4999 вполне достаточно для установки до 2000 соединений в секунду с системами, не поддерживающими timestamp. Проще говоря, этого вполне достаточно для большинства применений.

По умолчанию там такой диапазон:

Для тяжелых проектов диапазон рекомендуется увеличить:

tcp_tw_reuse

По умолчанию отключена:

tcp_window_scaling

По умолчанию она включена:

Лучше так и оставить:

tcp_rfc1337

По умолчанию опция отключена:

На сервере она точно не помешает:

ip_forward

По умолчанию переадресация включена:

Если сервер не является маршрутизатором, то включать эту опцию нет необходимости:

tcp_abort_on_overflow

Если ситуация требует, то ее можно включить:

Окно перегрузки в TCP

p, blockquote 10,0,0,0,0 -->

Окно потоком управления и перегрузки в TCP

p, blockquote 11,0,0,0,0 -->

Окно перегрузки, существует на стороне отправителя, его размер рассчитывается отправителем в зависимости от того, какая нагрузка на сеть, а не от того сколько данных может принять приложение. Приложение может быть готово принять много данных, но сеть загружена, в этом случае отправлять так много данных не имеет смысла.

p, blockquote 12,0,0,0,0 -->

Сигнал о перегрузке

Следующий вариант, как отправитель может узнать о перегрузке это явный сигнал от маршрутизатора. Однако для этого маршрутизаторы должны поддерживать отправку сигналов. Одним из возможных вариантов является технология Random Early Detection, при этом маршрутизатор с некоторой вероятностью начинает отбрасывать пакеты еще до того как буфер полностью заполнен и началась перегрузка.

p, blockquote 39,0,0,1,0 -->

В результате отправители узнают о возможной перегрузке по потере сегмента ещё до того, как она произошла, и получают возможность заранее уменьшить окно перегрузки. Но это не явный тип сигнала, технологиях Explicit Congestion Notification, обеспечивает явную отправку сигнала от маршрутизатора к отправителю, о том что в сети происходит перегрузка.

p, blockquote 40,0,0,0,0 -->

Explicit Congestion Notification (ECN)

Рассмотрим на схеме, как она работает. Отправитель передает сегмент в сеть, который доходит до маршрутизатора. Маршрутизатор находится в состоянии близкому к перегрузке, буфер заполнен, но не полностью. Для того чтобы предупредить отправителя о перегрузке в сети, маршрутизатор устанавливать специальные флаги в заголовке IP, которые говорят о том, что в сети произошла перегрузка.

p, blockquote 41,0,0,0,0 -->

p, blockquote 42,0,0,0,0 -->

Сегмент передается по сети дальше и достигает получателя. Получатель в заголовке IP видит что установлен флаг, свидетельствующий о перегрузке, для того чтобы о перегрузке узнал не только получатель, но и отправитель, получатель устанавливает соответствующие флаги уже в заголовке TCP, когда передает подтверждение.

p, blockquote 43,0,0,0,0 -->

 Explicit Congestion Notification

p, blockquote 44,0,0,0,0 -->

p, blockquote 45,0,0,0,0 -->

ECN в заголовке IP

Рассмотрим, какие поля в заголовке IP и TCP используются в технологии Explicit Congestion Notification. В заголовке IP используются 2 бита в поле тип сервиса, значение 00 говорит о том, что перегрузки нет, а 11 означают что перегрузка произошла.

p, blockquote 46,0,0,0,0 -->

ECN в заголовке IP

p, blockquote 47,0,0,0,0 -->

ECN в заголовке TCP

В заголовке TCP для этих целей используются три флага, NS, CWR, ECE.

p, blockquote 48,0,0,0,0 -->

ECN в заголовке TCP

Как применить все эти значения разом, не сохраняя их?

Для удобства ниже приведены все параметры разом, если решение вам нужно здесь и сейчас:

Как сохранить эти значения?

Если сервер работает корректно со всеми настройками указанными выше, то для их сохранения достаточно добавить все эти переменные в конец файла /etc/sysctl.conf:

Заключение

В прошлой статье мы рассматривали управление потоком в TCP, это способ предотвращения отправки в сеть слишком большого количества сегментов, которые не могут быть приняты получателем, у которого просто недостаточно и места в буфере.

p, blockquote 1,0,0,0,0 -->

Протокол TCP управление перегрузкой

p, blockquote 2,0,0,0,0 -->

Получатель, при отправке каждого подтверждения, указывает в сегменте размер окна, количество байт, которые он может принять. Отправлять больше данных в сеть не имеет смысла.

p, blockquote 3,0,0,0,0 -->

Управление потоком в TCp

p, blockquote 4,0,0,0,0 -->

Но возможна и другая проблема. В буфере получателя может быть достаточно свободного места, но сеть, через которую передаются данные, перегружена.

p, blockquote 5,0,0,0,0 -->

Сеть перегружена

p, blockquote 6,0,0,0,0 -->

Одновременно, большое количество компьютеров решили передавать данные, маршрутизаторы не способны передать такое большое количество пакетов в единицу времени и они вынуждены отбрасывать пакеты. В этом случае происходит перегрузка, отправители передают в сеть большое количество данных, однако большая часть из этих данных отбрасываются маршрутизаторами и не доходит до получателей. Таким образом, большая часть каналов сети занята, но полезные данные от отправителя до получателя почти не доходят.

p, blockquote 7,0,0,0,0 -->

Итоги

p, blockquote 50,0,0,0,0 -->

TCP для определения размера окна перегрузки, использует комбинацию двух методов, аддитивное увеличение мультипликативное уменьшение и медленный старт. Сначала работает медленный старт, затем происходит переход на аддитивное увеличение мультипликативное уменьшение. Основным сигналом перегрузки являются потеря сегментов в сети, но также существуют и другие сигналы, эта задержка сегментов и явный сигнал от маршрутизатора.

p, blockquote 51,0,0,0,0 --> p, blockquote 52,0,0,0,1 -->

Как TCP решает проблемы потери пакетов

Теперь мы поговорим о TCP, то есть о нашем четвертом уровне. За следующие десять минут я расскажу, как работает TCP.


Когда мы в гостях, то просим кого-то передать соль. Когда человек нам передает соль, мы подтверждаем, что соль до нас дошла. В данном случае мы также берем какой-то сегмент, передаем его, ждем подтверждения. Опять передаем. И если произошла потеря, то мы пересылаем этот сегмент и в итоге он у нас доставляется. Такая техника отправки одного сегмента называется Stop and wait.

Но наши сети за последние 30 лет очень сильно ускорились. Возможно, кто-то из вас помнит диалап, интернет по мегабайтам. Сейчас вы, возможно, уже можете подключить домой гигабитный интернет.

Также мы можем в этом случае начать отправлять сразу по несколько пакетов. В нашем примере их три. Мы отправляем окно в виде трех пакетов и ждем, когда они все подтвердятся.

В случае потери пакета мы можем начать переотправлять все пакеты, начиная с первой нашей потери. Эта техника называется Go-Back N. А в другой ситуации мы можем начать отслеживать все пакеты и пересылать только те, которые были потеряны. Такая техника называется Selective Repeat. Она более дорогая со стороны сервера. Когда мы готовили слайды, ушло много времени на то, чтобы понять, как ее представить. Я сам в ней запутался, и поэтому придумал такую аналогию.

Есть всем нам известные трубы, через которые идет вода. Трубы имеют разный диаметр, где-то они могут быть тоньше, и в данном случае самое узкое место будет как раз с нашей максимальной пропускной способностью. Мы не сможем налить больше воды, чем позволяет это узкое место.

Мы попробуем стрелять шариками слева направо. С правой стороны нам будут подтверждать, что шарики долетели. Мы начинаем слать поток шариков. Посмотрим на его срез. Вот шарики летят в одну сторону, подтверждаются, и количество наших шариков экспоненциально растет. В какой-то момент объем шариков становится настолько большим, что они замедляются и начинаются потери. После потери мы немножко замедляемся, уменьшаем наше окно в два раза. Затем мы пытаемся понять, что с нами произошло. Первая стадия называется TCP Slow Start.

Когда мы схлопнули окно в два раза, мы можем восстановить соединение и попросить ребят переслать нам наши шарики заново. Они кричат нам, что надо переслать шарики, мы им отвечаем — вот ваши шарики. Такая фаза называется Fast Recovery и Fast Retransmit.

Когда мы поняли, что у нас все хорошо, мы начинаем постепенно наращивать количество отправляемых шариков, начиная с того схлопнутого окна. Такая фаза называется Congestion Avoidance. То есть мы пытаемся избежать потерь наших пакетов.

Фаза схлопывания нашего окна в два раза называется Multiplicative decrease. А медленная фаза наращивания количества шариков называется Additive Increase.

Когда наш Congestion Avoidance опять потеряет пакеты, мы можем произвести следующую стадию. Но в данный момент нас больше интересует само изображение этого графика. Мы видим такую пилу, и эта пила нам еще несколько раз пригодится. Запомните, как она выглядит.


Мы вернемся к проблемам обычных TCP-протоколов. По аналогии с трубой мы наливаем пакеты. Так как в интернете кроме нас существуют еще другие пользователи, они тоже начинают наливать пакеты в трубу. В какой-то момент буферы наших роутеров могут переполниться и создать нам проблемы отправки пакетов.

Также существует проблема с потерей пакетов в беспроводных сетях. Скорее всего, в вашем ноутбуке нет Ethernet-порта и вы смотрите доклад через Wi-Fi. Потери пакетов в Wi-Fi и в мобильных сетях возникают не по причине самого маршрутизирующего устройства, а по причине радиопомех. Такая метрика будет нам не очень полезна.

Отличие TCP BBR от других алгоритмов

Тут мы подошли к BBR. Он расшифровывается как Bottleneck Bandwidth and Round Trip Time, это метрики ширины канала, когда мы не забиваем наш канал полностью, и времени путешествия пакета от нас к серверу и обратно.

Когда мы отправляем данные, то идеальное состояние, при котором пакеты находятся в стабильном состоянии, летят и еще не были подтверждены, называется Bandwidth-delay product. BDP мы можем увеличивать за счет использования буферов сетевых устройств. Когда этот буфер превышается, начинаются потери.

И обычные TCP-алгоритмы как раз работают с правой стороны графика, то есть там, где происходят потери — мы наливаем так много пакетов, что потери неизбужны. Пакеты замедляются, и мы начинаем схлопывать окно.

BBR, в свою очередь, работает по другому принципу, близкому к нашей трубе. Мы просто наливаем то количество пакетов, которое можем пропустить. В фазе старта, то есть в самом начале, мы наливаем пакеты до тех пор, пока не начнутся заторы.

И иногда возможны потери пакетов. Но BBR пытается избежать этого момента. Когда мы заполнили нашу трубу, мы начинаем откатываться. Эта фаза называется drain.

Мы возвращаемся к нашему стабильному соединению, где будет полностью заполнена, но при этом мы не будем использовать дополнительные буферы, дополнительные резервуары. Из этого положения BBR продолжает работать.

Периодически мы будем смотреть на то, что происходит с нашей сетью. Мы отслеживаем практически каждый пакет, который к нам возвращается. Когда к нам вернулись пакеты, мы начинаем пробовать немножко ускорять количество пакетов, ускорять сами пакеты, отправляя их в сеть.

И если у нас не происходит никаких проблем, мы можем остаться на этом значении. То есть продолжить работать с тем темпом, который нам комфортен. Если же все-таки произошли потери, мы можем откатиться назад.

Когда мы получили подтверждение, увидели, что скорость улучшилась, мы можем немножко подождать, посмотреть на промежуток десять секунд. И если на этом промежутке мы увидим, что скорость отправки пакетов возрастает и пакеты быстрее подтверждаются, то мы можем войти в фазу probe RTT и проверить, не стало ли все еще лучше.

Такие фазы чередуются, то есть мы будем постоянно проверять, что у нас с сетью.

Алгоритм BBR основан уже не на потере пакетов, а как раз на ширине канала и времени путешествия пакета.


Фактически он неуязвим для потери пакетов. Он на них практически не реагирует, и из-за этого у нас возникают некоторые проблемы. Google обещал, что эти проблемы будут исправлены в BBR v2.

Мы рассмотрели наши фазы, и перед нами опять гребенка, которую я уже показывал. Красным выделены обычные TCP -протоколы. Вот он набирает, набирает, замедляется, и опять теряет пакеты. А BBR выставляет такой темп, который ему нужен, с которым он будет работать все время, и постоянно проверяет нашу сеть, не стала ли она чуть лучше. И, возможно, ускоряется.

Наши метрики постоянно обновляются, мы отслеживаем каждое подтверждение со стороны клиента и проверяем, ускорилась наша сеть или нет.

Чем же управляется этот темп отправки пакетов? Мы управляем темпом отправки с помощью техники pacing. Она реализована в планировщике, о котором я уже говорил. Это планировщик FQ. Также она реализована в самом ядре, но об этом я расскажу позже.

Мы стараемся, как в трубе, налить больше данных, и при этом не замедляться, не терять наши пакеты. Но BBR не так прост. Скорее всего, вы живете в контейнерах либо используете несколько серверов для баз данных — возможно, для картинок.


И все эти сервера взаимодействуют между собой. Там включен обычный TCP, не BBR. И когда у вас появится пила, которую мы уже видели, когда начнет схлопываться окно, то, возможно, BBR начнет нащупывать, что окно схлопывается, и увеличивать темп отправки пакетов. Тем самым он будет вытеснять обычные TCP из нашей сети, доминировать.

Если сеть будет совсем плохой, возможны другие проблемы. Обычные TCP вообще не будут работать, а так как BBR практически не чувствителен к потере пакетов, он с определенным темпом все же продолжит работать.

Эту проблему с data-центрами мы можем решить опцией TCP_CONGESTION. Она выставляется на каждый сокет, на каждое соединение. Сейчас, насколько я знаю, эта опция не реализована практически ни в одном веб-сервере. А наш L7-балансировщик ее поддерживает. Но вернемся к нашему pacing. Если вы работаете со старыми ядрами, то в ядрах до версии 4.20 была ошибка в реализации pacing. В этом случае стоит использовать планировщик FQ.


Теперь вы знаете, как работает TCP, можете прийти к своему системному администратору и рассказать ему, почему стоит включить BBR.

Вернемся к нашим десяти процентам. Откуда они могут появиться? Сети со стороны операторов сейчас очень большие. Все упирается в основном в деньги. Вы можете построить каналы на 100, 200 терабит и пропускать огромное количество 4K-видео, например. Но ваш клиент все равно будет находиться в конечной точке.

И скорее всего, эта последняя миля до клиента будет источником проблем. Все наши Wi-Fi и LTE будут терять пакеты. В случае использования обычного TCP мы будем видеть замедления. BBR решает эту проблему. Вы можете его включить всего лишь за счет двух команд, которые я указал. Всем спасибо.

Читайте также: