В Тензоре около 7 тысяч сотрудников и более 100 филиалов по всей стране - такой компании категорически необходима видеокоммуникация. Существует 2 соизмеримых по издержкам решения: использовать существующий продукт или реализовать свой.
В этой статье я, разработчик отдела вебинаров, расскажу, каким образом наша компания, выбрав когда-то второй путь, пришла к собственному сервису вебинаров. Пройдя несколько итераций развития, на сегодняшний день мы научились проводить видеоконференции буквально на всех наших сотрудников.
Дисклеймер: здесь и далее мы будем говорить про веб-решения видеосвязи, основанные на технологии WebRTC. Всё, что нужно о ней знать для понимания статьи: WebRTC - технология, позволяющая устанавливать P2P соединение между двумя браузерами и обмениваться данными: медиа-потоками, такими как изображение с камеры, звук с микрофона, демонстрацию вашего экрана и др, а также потоками любых других данных. Более подробно с ней можно ознакомиться на официальном сайте: https://webrtc.org/.
Позвольте начать с предыстории.
Отдел вебинаров. Начало (2017 год)
Собственная видеосвязь в Тензоре существовала уже несколько лет, позволяла собирать в одном звонке до 50-60 человек. Это покрывало все потребности компании, оставляя нерешёнными всего 2 проблемы:
Проведение массовых конференций и онлайн-корпоративов.
В Тензоре всё чаще стала возникать потребность в видео-конференциях с большим количеством слушателей, разбросанных по всей стране. Очевидно, что собрать их всех в одном месте можно только в интернете.
Постепенный рост отделов и среднего числа участников совещаний.
К концу 2016 года эта проблема становиться особенно острой, так как созвонов где одновременно вещают 50-60 человек становится недостаточно. Компания расширяется, появляются новые отделы, старые увеличиваются и делятся на подотделы… И всем по-прежнему нужно проводить планёрки с максимальной явкой, а сервера уже работают на пределе возможностей.
Могли ли мы тогда просто масштабироваться? Давайте посчитаем.
Что такое полноценный видеозвонок? Видеосовещание, в котором каждый участник, с одной стороны, должен иметь возможность показывать свою камеру, а с другой - видеть всех остальных.
Как бы ни была организована обработка потоков на сервере, от каждого участника идёт хотя бы 1 поток и каждому нужно отправлять хотя бы n-1 потоков (по одному от каждого другого). Иначе говоря, рост потребляемых ресурсов был квадратичным по отношению к увеличению числа участников.
Таким образом, потенциал для масштабирования был, но был он небольшой и очень дорогой: потратить круглую сумму, чтобы отсрочить проблему на 1-2 года.
Очень быстро стало понятно, что на этом далеко не уехать, необходимо принципиально новое решение, если мы хотим преодолеть барьер по числу участников. Именно тогда наш доблестный теперь уже сеньор-разработчик (Влад, привет!) предлагает амбициозный проект сервиса вебинаров.
Действительно, ведь чем вебинар принципиально отличается от звонка? Теперь только некоторая группа участников в совещании являются активными, все остальные наблюдают за их разговором. По сути вебинар - это звонок, который транслируется на максимально возможное количество зрителей. Ключевой особенностью была механика запроса голоса: любой зритель мог запросить слово и - с разрешения ведущего - стать спикером, установив с сервером WebRTC соединение.
В конце лета того же года было принято судьбоносное решение в компании: сформировать команду, которая в кратчайшие сроки должна предоставить прототип сервиса. Так появляется наш отдел Вебинаров!
Рецепт первых вебинаров
Глобально, сервис должен состоять, как минимум, из трёх частей:
Модуль приёма видеопотока от ведущего (ведущих) по WebRTC.
Модуль отправки этого потока зрителям.
Модуль управления трансляцией.
С первым модулем задача уже была решена в звонках, нам нужно было лишь перенять от них опыт - подготовить медиа-сервер для приёма потоков.
Всё самое интересное предстояло решить со вторым:
Что модуль отправки получал на вход? Открываем спецификацию WebRTC и видим, что поток сжимается видеокодеком VP8 и аудиокодеком Vorbis - всё это оборачивается в контейнер .webm.
В каком виде этот поток следует отправлять зрителям? Как бы сильно нам ни хотелось отправлять «в таком же» формате, увы, вышеупомянутые кодеки не поддерживались IE и Safari (нам важна была поддержка и первого, и второго браузера). По этой причине у нас сразу же появляется задача конвертации .webm (VP8/Vorbis) в .mp4 (H264/AAC). Для этого, очевидно, понадобится одноимённый компонент конвертор. При этом видео для отправки необходимо будет разбивать на сегменты (чанки), что и послужит в дальнейшем названием микросервиса.
А как именно отправлять поток зрителям? Очевидный ответ - по WebRTC. Догадаетесь, откуда полетят палки в колёса?… Если Apple уже летом 2017 анонсирует поддержку технологии в 11-м сафари, то IE… без шансов. Поэтому для такого случая модуль должен уметь отправлять поток резервным способом - мы выбрали для этого websocket.
Что касается третьего модуля, здесь всё просто: у ведущего должна быть возможность начинать/завершать трансляцию, ставить её на паузу. Сервер обработки сигналов с клиента мы так и назвали - сигналлер (signaller).
Таким образом, у нас уже намечалась следующая микросервисная архитектура:
Интерактивность
Очень скоро мы решили, что одной трансляции камеры ведущего будет недостаточно, поэтому сразу заложили дополнительные возможности: демонстрацию экрана, демонстрацию презентации и доску для рисования.
С трансляцией последних двух нас здорово выручал canvas: его механизм захватывал всё происходящее, и этот стрим можно было отправлять вместе со стримом ведущего.
Иными словами, браузерное API до неприличия непринуждённо закрывало нам все вопросы со стримами:
getUserMedia() - метод захвата стрима с микрофона и камеры
getDisplayMedia() - метод захвата стрима с окна или экрана
canvas.captureStream() - метод захвата стрима с канваса
Наконец, какой вебинар может пройти без чата зрителей? С этим оказалось всё намного проще: функционал уже был в звонках, им занимался другой отдел, и нам любезно предоставили ту же возможность, дав сконцентрироваться именно на трансляции :)
Запрос голоса
В то же время нужно было сохранить возможность выступить любому участнику вебинара, не только ведущему. Для этого мы сделали механизм запроса голоса: зритель, запрашивая слово, подключался к медиа-серверу тем же механизмом, что и ведущий. В результате они могли общаться будто в звонке, а их видеопотоки отправлялись зрителям, что позволяло наблюдать за диалогом.
Что по трафику?
Как видно, в нашей первоначальной схеме чанкер честно отдаёт видеопоток (стрим) каждому зрителю. Вместе с ростом их числа назревал вопрос: а как себя почувствует корпоративная сеть в филиалах, когда все сотрудники одного офиса единовременно начнут смотреть вебинар, т.е. загружать из внешней сети FullHD стрим?
Нужно было разработать решение, которое бы снизило трафик внутри локальной сети. К счастью, на помощь пришёл сам WebRTC: тот факт, что мы можем устанавливать прямое соединение между двумя клиентами, позволил реализовать т.н. P2P-дерево доставки контента.
При таком подходе зритель, получая контент от чанкера, может сам выступать в роли отправляющего другим участникам. Безусловно, каждое дополнительное WebRTC соединение повышало нагрузку на него, поэтому один участник мог раздавать стрим максимум трём другим. Последние могли раздавать следующим - отсюда и получалось дерево.
Вебинары. Первый релиз (лето 2017 года)
Итак, нашей команде понадобилось всего полгода, чтобы на свет появилась первая полноценная версия сервиса!
Пока компания привыкала к вебинарам, у нас стояла только одна задача: исследовать нагрузку с реальных зрителей, понять текущий максимум, определить направления оптимизации.
Результат первых «боевых» испытаний: 450 зрителей в одном вебинаре. Позже мы смогли зафиксировать рекорд в 500 человек, дальше чанкер начинает умирать.
Это, конечно, был колоссальный результат на фоне предыдущего барьера звонков. Но, очевидно, что потенциал вебинаров был совсем не раскрыт. Нужно двигаться в направлении оптимизации - просто масштабировать систему пока рано.
Во что мы упирались? Однозначно в раздачу контента.
Как разгрузить чанкер?
После релиза мы также убедились в полной работоспособности идеи P2P-дерева: контент действительно раздавался между зрителями, и это снижало не только нагрузку на сеть, но и нагрузку на чанкер.
Решили взять эту идею в основу дальнейшей оптимизации. Зритель по-прежнему раздавал стрим не более трём другим участникам. Но что, если сделать микросервис, который был бы готов раздавать большему числу зрителей?
Так у нас появился репитер (repeater).
Репитер (лето 2018 - 2019)
Микросервис разворачивался внутри локальной сети филиала. Технически он ничем не отличался от обычного зрителя кроме одного: на нём не было лимита подключений для P2P.
При этом мы скорректировали изначальный алгоритм работы дерева, и теперь каждый зритель сначала пытался подключиться именно к репитеру и только в случае неудачи искал другого «раздающего».
Таким образом, получилось снизить нагрузку и на чанкер, и на каждого клиента в среднем. Трафик локальной сети сконцентрировали в одном направлении, а главное - открыли перспективу масштабирования: гораздо проще увеличивать теоретический пул репитеров в филиале, чем создавать тот же пул чанкеров и систему обмена контентом между ними.
Первая тысяча
Сразу после внедрения репитера мы фиксируем на вебинаре первую тысячу участников! В тот момент - грандиозный успех! Руководители секторов становились смелее и начинали планировать вебинары на 1500-2000 человек - и такое количество нам тоже удавалось держать!
Помните 2 проблемы в начале статьи? Уже на этом этапе они были решены: крупные совещания прекрасно проводились в рамках вебинара, а большие корпоративные конференции, пусть не на всю компанию, но на треть, тоже были возможны!
Разумеется, на этом никто не собирался останавливаться, но спросите нас тогда - что мешало сервису держать на несколько тысяч человек больше? Мы бы ответили - дайте чуть больше железок… Увеличим чуть больше репитеров, нарастим мощность, и всё - весь Тензор в одном вебинаре.
Необходимости такой не было, решение временно отложили…
Свободное развитие (2019)
Безусловно, это самый спокойный период в истории нашей команды. Основная проблема в то время - баги, которые периодически находили тестировщики. Естественно, мы их исправляли.
Вебинарами в компании пользовались всё чаще: у сотрудников уже появлялись идеи для внутренних тренингов, проводились в том числе и развлекательные мероприятия (Впервые провели новогодний 5-тичасовой эфир Тензор ТВ на всю компанию).
В сравнении с бурным стартом мы действительно спокойно развивали продукт:
1. Во всю улучшали фронтенд.
Компания активно использовала свой фреймворк Wasaby (https://wasaby.dev), - у нас была возможность внедрять новые фишки в числе первых отделов.
2. Добавили демонстрацию видеофайлов.
Функционал демонстрации презентаций уже был, почему бы не сделать то же самое с видео? Технически их реализации мало чем отличались: компонент video имеет точно такой же метод captureStream(), как и canvas, т.е. трансляция велась с плеера ведущего.
3. Делали ресёрч.
Было достаточно ресурсов для ресёрча повышения доступности вебинаров: на тот момент для бесперебойного просмотра нужен был стабильный быстрый интернет-канал, но как сделать трансляцию, например, для 3G?
На тот момент набирали популярность технологии адаптивной потоковой передачи данных (Adaptive Bitrate Streaming, ABR) - открытая MPEG-DASH и проприетарная HLS от Apple. Идея механизма заключалась в раздаче стримов сразу в нескольких аудио и видео дорожках (качествах): от 240p до 1080p. Зритель мог бы сам переключаться между ними или довериться алгоритму плеера для выбора оптимального качества (недавно у нас вышла статья на эту тему - ABR для живых трансляций).
Пожалуй, самый неудобный вопрос к нашему сервису в то время был следующий: хорошо, репитер действительно закрыл проблемы нагрузки в филиалах, но как быть с удалёнными сотрудниками? Им ведь приходится подключаться напрямую к чанкеру? Что, если их количество увеличится?
К 2019 году доля удалённых сотрудников компании была невелика. Именно к этому мы и апеллировали: в Тензоре абсолютное большинство очных работников, предпосылок к изменениям в этом никаких не наблюдается.
В конце концов, что такого должно произойти в мире, чтобы количество дистанционных сотрудников стало на порядок больше обычных?…
2020
«That would be funny. If it weren't so sad.» Выбора не было – с приходом тотальной удалёнки в репитере исчез смысл, P2P-дерево не работало.
По возможностям мы откатились не просто к моменту старта, всё было ещё хуже - в лучшем случае чанкер выдерживал 200 человек. Это была катастрофа!
Обнаружилось ещё и то, что не у всех сотрудников хороший домашний интернет. Поэтому помимо проблемы с нагрузкой чанкера актуальной стала задача адаптивного стриминга, ресёрчем чего мы, к счастью, уже занимались.
В вебинарах предстояло много инноваций.
Рождение ориджина
С самого начала наша архитектура предполагала наличие активного соединения со зрителем - непосредственная отправка стрима по WebRTC или websocket.
Однако существовал принципиально другой подход: складывать чанки на ресурсе и отправлять зрителям ссылку на них. В этом случае, клиент будет скачивать его по HTTP, как статику с кеш-сервера. Ключевой момент: нет никакого постоянного соединения между ориджином и клиентом.
Эта фишка идеально сочеталась с ABR: зритель получал ссылку на плейлист чанков (.mpd файл в случае MPEG-DASH и .m3u8 в случае HLS), внутри которого содержалась информация о всех вариантах качества стрима, а также ссылки, по которым их можно загружать.
Основное преимущество такого микросервиса - простейшая масштабируемость: чанки складываются на основном (origin) сервере, пользователи же обращаются к edge серверам. Если у edge сервера отсутствует конкретный чанк, он загружает его с ориджина и отдаёт зрителю. Следующим зрителям он сразу отдаст этот сегмент.
Стало ясно, что с чанкером пора прощаться. Осталось только из WebRTC-потока ведущего научиться делать ABR.
От конвертера к транскодеру
Одно дело - конвертировать .webm в .mp4, совсем другое - делать из потока сразу несколько дорожек по стандарту ABR. Это уже задача транскодирования, ввиду чего нужен был новый сервис - транскодер (transcoder).
От сигналлера к бизнес-логике (БЛ)
Зрители как-то должны получать ссылку на стрим. Если раньше у них было активное соединение с чанкером, теперь хотелось бы дать API для получения информации о стримах. Так мы трансформировали сигналлер в бизнес-логику.
Вебинары. Второе поколение (2020 год)
Спустя первую половину этого кошмарного года архитектура стала выглядеть следующим образом:
Но это того стоило!
Результаты после релиза второго поколения, 2000 зрителей. Вернулись к показателям прошлого года. Только теперь сотрудники были «разбросаны по интернету».
Догадываетесь, какая приоритетная задача стала у нас следующей?
Проект «10к зрителей на один вебинар» (2021 год)
Очевидно, что нашей следующей задачей становится полное покрытие штата компании. Так появился проект с говорящим названием.
Суть выражалась кратко: всё оптимизировать и масштабироваться, - что может быть проще? Тем не менее, вот три важных момента:
Минимизация расходов на железо. Нужно было пройтись шлифовкой по всем микросервисам: где-то устранить неоправданно высокий жор ресурсов, где-то просто оптимизировать работу.
Масштабирование кеша. Ориджин отлично справлялся со своей работой раздачи стримов, но оказалось, что более выгодно купить внешний CDN, чем разворачивать дополнительные мощности у себя.
P2P. Сотрудники потихоньку возвращаются в офис, а значит, становится актуальным знакомый механизм. Необходимо по максимуму оптимизировать его алгоритмы.
Указанное число зрителей бралось с приличным запасом: на момент старта проекта число сотрудников компании только-только приближалось к отметке 6000. И понятно: для проверки достижения именно этого числа понадобится нагрузочное тестирование с применением зрителей-ботов.
Как вы думаете, что в этом проекте было самым сложным? Вы правы - провести это самое нагрузочное тестирование! Продемонстрировать, что мы достигли цели, и можем собрать всех сотрудников на один вебинар!
Оказалось, что раньше, не имея такой возможности, никто и не планировал никаких всеобщих конференций - они просто не предусматривались!
Компания уже проводила стабильные трансляции, на которых присутствовали по 3-4 тысячи человек, но для сдачи проекта всё равно нужно было набрать 10 тысяч.
На момент 2021 года мы не нашли сервис ботов, который бы предоставил их в таком количестве - максимум 4 тысячи. Следовательно, нам точно нужны были все компьютеры компании. Сначала хотели, чтобы одним вечером все сотрудники всех филиалов под конец рабочего дня не выключали машины, чтобы мы могли их использовать в нашем нагрузе, но многие всё равно этого не сделали. В конце концов, безопасники фокус не оценили, сказали «небезопасно» (и, в общем-то, были правы) – фокус не удался :) Далее в компании был объявлен день единого вебинара - день, когда все сотрудники всех подразделений должны зайти на вебинар и посмотреть, функционирует ли он конкретно в их браузере на их компьютере.
В Тензоре много отделов, и сотрудники не обязаны знать про каждый. Но вот с того момента в компании не осталось ни одного человека, кто не слышал бы про нас.
Вебинары 3. На пути к совершенству (2022 год - наст. время)
Преодолев отметку в 10 тысяч зрителей, мы смогли сконцентрироваться на фичах нашего сервиса. Архитектура оставалась прежней, но, тем не менее, произошло событие, ознаменовавшее новое поколение вебинаров - переход фронтенда на ReactJS.
Вместе с этим сразу адаптировали интерфейс к мобильным устройствам - теперь стало возможным смотреть вебинар через браузер телефона (в начале 2023 мы сделали возможным даже проводить его с телефона).
Транскодер тоже получил новые фичи. Далеко не полный список:
Научился транслировать видеофайл сам.
Раньше мы транслировали его напрямую из плеера ведущего, теперь же транскодер получает сигналы «перемотай», «останови», а зрители видят нужную секунду видеофайла.
Научился работать с видеокодером (OBS).
Вебинары всё больше становились похожи на стриминговые платформы - мы решили сделать возможным использовать тот же инструментарий.
Научился накладывать виртуальный фон.
Уверен, у каждого сотрудника одно из ярких воспоминаний года удалёнки - виртуальные фоны, скрывающие бардак на заднем плане. Теперь это стало возможным сделать и у нас!
Заключение
Чем же занимаемся мы сейчас? «Быстрее, выше, сильнее». Теперь наша задача - сделать продукт предельно отказоустойчивым.
Тернистый путь к сегодняшним вебинарам занял у нас почти 7 лет и 3 большие итерации. Могли мы сразу разработать текущую архитектуру? Однозначно нет. WebRTC - одна из самых молодых веб-технологий, и мы развивались буквально вместе с ней (более того - развиваемся до сих пор).
Чему мы научились за эти годы?
Пробовать разные популярные медиа-серверы в поисках максимально подходящего под наши нужды.
Распотрошили полностью ffmpeg в собственном форке - не бойтесь брать такого масштаба open-source проекты и делать его под себя.
Пришли к внешнему CDN кешу: отказ от внешнего сервиса видеосвязи не означает, что мы не можем использовать внешний сервис кеширования. Это решение оказалось более дешёвым и простым.
Всей этой истории и нашего отдела не было бы, выбери тогда компания сторонний сервис видеосвязи.
Наконец, самое главное, чего мы достигли - полностью закрыли вопрос видеокоммуникации в нашей компании и сделали это даже с запасом!