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

У нас в Skyeng есть групповые уроки английского, они ограничены 10 участниками. Поскольку мы не используем промежуточного преобразования сигнала, а подключаем каждого пользователя, используя SFU, получается, что каждый генерирует один исходящий поток и принимает девять входящих потоков трафика. Также наши SFU сервера записывают уроки на случай каких-то сложностей с учителем (то есть для контроля качества) и для анализа различных показателей урока.

Мы учим не только английскому, но и математике, и другим предметам. Вдруг выяснилось, что для ряда занятий нужно собирать больше 10 человек, и при этом нужно иметь возможность разговаривать с каждым. Понятно, что учителю можно дать толстый канал в 1 Мбит/с, а ученикам — каналы потоньше: в 144p или 240p, например, но всё равно квадратичный рост трафика выглядел угрожающе.

Я решил протестировать Janus-видеосервер на 100 пользователях в канале и посмотреть, как быстро он упадёт. Ситуация осложнилась тем, что справки про это нет, примеров нет, и готовых решений тоже пока нет. Поэтому я и пишу.

Вы же тоже хотите посмотреть, как быстро он упадёт, да?

С чего начали тесты


Для начала взяли Selenium WebDriver, запустили несколько браузеров и создали несколько подключений к уроку. Всё закончилось довольно быстро и предсказуемо тем, что на 11 пользователях десктопный компьютер с браузерами превратился в тыкву. Правда, было не очень понятно, что именно превратило его в эту самую тыкву: медленный ответ сервера или нагрузка на процессор.

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

  1. Один исходящий канал трафика и N-1 — входящих, где N — это число участников конференции. Это соединение через SFU.
  2. Или один исходящий канал и один — входящий, где входящий — это MCU, то есть мультиплексирование всех входящих потоков на сервере.

Ещё бывают вариации (Simulcast и SVC), когда исходящих потоков несколько, например, в низком разрешении и в высоком. Таким образом, принимающая сторона может выбирать, с каким качеством воспроизводить медиапоток (как на Ютубе). Если у вас MCU, то сервер будет стоить дорого, так как нужны мощные процессоры для перекодирования медиапотоков в реальном времени. Если у вас SFU, то с сервера будет очень много исходящего трафика, так как каждый участник получает копию всех медиапотоков, которые приходят на сервер. Если у вас Simulcast и SVC, то они должны поддерживаться софтом клиента.

Мы используем SFU, то есть в середине стоит сервер, который пропускает через себя все эти потоки, но мы их никак не преобразуем. Со стороны клиента мы можем говорить серверу, от каких именно пользователей мы хотим получать медиаданные. То есть если участник конференции молчит и для отображения его элемента с видео нет места на экране пользователя, то мы говорим серверу не отправлять нам видео этого участника. Если нет потребности видеть все 100 человек сразу (а такой потребности в самом деле нет), а можно видеть шесть–десять последних говоривших, то и входящих потоков на пользователя надо явно меньше 100.

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

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

Если Selenium не тянет на одной машине, то пусть запускает безголовые браузеры на сервере. Внятной документации, как это делать, я не нашёл, примеров тоже не нашёл. На форумах спросил, в Google Meet-группе про тестирование Janus-cерверов тоже спросил. Тишина. Поэтому мы решили спросить, как стримеры тестируют свои платформы на Janus.

Нашли опытного человека Евгения, который рассказал, что у них стриминговая платформа. Это, по сути, один паблишер (участник, отправляющий аудио и видео) и сотни принимающих. Эту схему он тестировал связкой Janus-серверов. Один сервер с Janus у него отправляет, другой сервер с Janus у него принимает. В итоге он смог очень хорошо подтюнить Janus потом.

Сами тесты


У нас 10 тысяч потоков в уроке на 100 человек. Решил сначала не добавлять логику о том, что надо отключать получение видео, если пользователь молчит. То есть в ходе теста хочу выяснить, какое максимальное количество пользователей на конференции может вытянуть сервер, если все будут отправлять видео и аудио и принимать видео и аудио от всех.

У нас в настройках Janus максимальный битрейт для входящего трафика от одного пользователя ограничен 256 Кб/с. То есть теоретический максимум для входящего трафика на сервер получается 100 пользователей * на 256 Кб/с = 25 Мб/с. Для исходящего трафика с сервера получается 100 пользователей * 99 потоков * 256 Кб/с = 2 500 Мб/с. На нашем сервере ограничение на канал стоит в 1 000 Мб/с, то есть уже в теории он не вывезет 100 пользователей. Но всё равно интересно посмотреть, сколько же реально пользователей получится вытянуть.

Мы взяли решение Евгения и немного доработали следующим образом:

— GStreamer на первом сервере отправляет видео и аудиопоток. На нём стоял Janus Streaming Plugin, который принимал поток, и уже этот поток можно было использовать для создания 100 пользователей. Также на этом сервере установлен Janus VideoRoom Plugin, который использовался, чтобы принимать медиапотоки с тестируемого сервера, то есть эмулировал приём видео и аудио браузером пользователей.

— Второй сервер — это сервер, который тестировали. На нём установлен Janus VideoRoom Plugin. В ходе теста планировалось создать на нём 100 паблишеров (они принимают входящие медиапотоки) и 9 900 сабскрайберов (они отдают исходящие медиапотоки).

Gstreamer генерил поток 30 кадров в секунду: там последовательность кадров типа крутящегося кружка с таймером. Битрейт на таком повыше реальности, потому что пользователи обычно не крутятся, а сидят достаточно неподвижно.



Сам тест написал на Go и запускал на своём компьютере. Тест по вебсокету подключался к первому серверу — Streaming Plugin, получал от него SDP Offer, подключался ко второму серверу (тоже по вебсокету), создавал паблишера через VideoRoom Plugin и передавал ему оффер от первого сервера, в ответ получал SDP Answer и передавал его на первый сервер в Streaming Plugin. В результате у нас получалось webrtc-соединение, через которое от первого сервера передавался медиатрафик на второй сервера, то есть входящее соединение для тестируемого сервера. И так — для каждого пользователя. Сервера стояли в одном ЦОДе рядом, чтобы минимизировать влияние сети.

Получали входящий поток видео и аудио — 100 входящих потоков на второй сервер. Осталось сгенерить ещё 9 900 исходящих потоков с сервера.

Создание исходящего соединения работает аналогично, только наоборот. Каждый раз, когда на тестовом сервере создавался паблишер, он отправлял сообщение в тест по вебсокету каждому подключённому пользователю. Тест на основе этого сообщения создавал сабскрайбера на тестовом сервере через VideoRoom Plugin, получал от него SDP Offer и создавал на первом сервере паблишера через VideoRoom Plugin, получал от него SDP Answer и отправлял сабскрайберу на втором сервере. То есть у нас получалось исходящее webrtc-соединение со второго сервера между его сабскрайбером и паблишером на первом сервере.

Потом я запустил тест на 100 пользователей на Intel® Xeon® CPU E5-1650 v4, 3.60GHz, CPUs 12, CPU cores physical 6, RAM 31.4 GB.

Сервер теста не прошёл. Точнее, мы остановились на 25 пользователях.



Стали теряться пакеты: Янус стал их дропать, потому что пакеты шли слишком долго. Непонятно, где они застревали, ещё нужно изучить. Опытным путём выяснилось, что наш сервер выдерживает 25 пользователей. Без потерь качества всё работает отлично. Таким образом, мы проверили, что может выдержать прод-сервер в плане нагрузки.

10 пользователей:





Всё гладко.

20 пользователей:





30 пользователей:







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

Но посмотрите, что происходит при нагрузке на 30 пользователей: она снижается. Это потому, что Janus выкидывает пакеты (в логах много предупреждений типа Discarding too old outgoing packet (age=6597865us). До 34-й минуты входящий трафик растёт, а потом падает, но исходящий трафик начинает падать уже на 33-й минуте, хотя должен был и дальше расти. Когда входящий растёт, а исходящий падает, — явно серверу поплохело.

25 пользователей:







27 пользователей:







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

Дальше


С битрейтом я пока не экспериментировал. Дальше хочу потестировать с разными битрейтами и ещё доработать тест, чтобы принимал не 99 потоков, а 10 видео- и 99 аудиопотоков: посмотреть, сколько пользователей может быть в комнате объективно в условиях «показываем только последних спикеров». Для обучения это нужно, например, на натурных уроках, когда учитель ходит по лесу, что-то собирает и показывает, а все могут с ним говорить и слышать друг друга.

Естественно, что всё запустилось не с первого раза. Из явных проблем было то, что сервер создаёт 10 тысяч соединений. Каждое соединение требует своего вебсокета, то есть на сервере быстро заканчивались порты, и он отказывался работать. Ещё порты по какой-то причине зависали в занятом состоянии, пока не перезапустишь nginx. То есть тест заканчивался, а порты не освобождались. В итоге я решил переписать тест, чтобы создавать для одного пользователя одно вебсокет-соединение (именно так работает на реальных уроках), плюс у каждого пользователя может быть до 100 медиапотоков. Так порты не заканчивались. Когда тест завершается или падает, порты освобождаются.

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

Комментарии (5)


  1. iwram
    11.02.2022 05:52

    А почему ничего не указали про libnice, настройки sysctl - должны быть кастомизации, если вы собираетесь тащить такую нагрузку? Возможно проблема не в janus. Также во воремя подобных тестов, хорошо использовать утилиты типа lsof (посмотреть занятые сокеты) и вывод бывает очень интересный. Также в обычном экспортере есть метрика node_sockstat_UDP_mem - может у вас там все возможные сокеты заняты, нет освобождения и поэтому идет сброс пакетов. Тут много факторов.

    Статья хорошая, но надо провести более обширное расследование (возможно провели, но не все отразили просто).


    1. bskton Автор
      11.02.2022 09:51
      +1

      Для тестов использовал libnice 0.1.18. Особо в нем не копался, поэтому ничего по нему сказать не могу. Про sysctl, lsof и node_sockstat_UDP_mem вообще не думал. Спасибо за подсказку :)

      Еще хочу погонять тесты и посмотреть perf-ом.


  1. FaceWithNoName
    11.02.2022 08:22

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

    Для воспроизведения трансляций (чтобы не использовать стриминг) можно еще mjr использовать, предварительно записать то что нужно и потом воспроизводить.


    1. bskton Автор
      11.02.2022 09:56

      В доке Janus видел, что стрим можно записать в mjr. Но что-то не видел чтобы можно mjr в качестве источника использовать для стрима. Это у Streaming plugin такая фича есть?

      Через mjr было бы гораздо удобнее, особенно если его зациклить можно :)


      1. FaceWithNoName
        11.02.2022 12:57

        Как минимум, воспроизведение есть в плагине recordplay. Можно оттуда взять код и воспроизводить где-то еще. Ну и там лежит просто rtp трафик по сути, можно и самому его куда-то слать (например, из бота), если захочется. На счет зациклить не уверен, timestamp придется тогда руками править скорее всего.