![](https://habrastorage.org/webt/59/e6/22/59e622d6100d3230732662.jpeg)
Где может потребоваться трансляция с гарантированной низкой задержкой? — на самом деле, много где. Например в онлайн видео-аукционах. Представьте себя ведущим такого мероприятия.
— «Двести тыыыысяч рааааз»
— «Продано!»
С высокой задержкой вы успеете сказать «двести тысяч три» и продать лот еще до того как видео дойдет до участников. Чтобы участники аукциона успели вовремя среагировать, задержка должна быть гарантированно низкой.
В общем, низкая задержка жизненно необходима в любом около игровом сценарии, будь-то онлайн видео аукцион, видеотрансляция скачек с лошадками или интеллектуальная онлайн игра «Что Где Почему» — и там и там требуется гарантированно низкая задержка и передача видео и аудио в реальном времени.
Почему CDN
Один сервер зачастую не способен доставить потоки всем желающим просто по той причине, что все зрители географически распределены и имеют каналы связи разной пропускной способности. Кроме этого, одного сервера может быть недостаточно по ресурсам. Поэтому нужна гроздь серверов, которые занимаются доставкой потока. А это и есть CDN — сеть доставки контента. Контентом в данном случае будет потоковое видео с низкой задержкой, которое передается с веб-камеры транслирующего пользователя зрителям.
В данной статье мы опишем процесс построения географически-распределенной мини-CDN и протестируем полученный результат. Наша CDN будет состоять из четырех серверов и работать по следующей схеме:
![](https://habrastorage.org/webt/59/e6/22/59e622d60ff09667209412.jpeg)
В этой схеме используется минимальное необходимое количество серверов: 4.
1) Origin + LB
Это сервер, который принимает транслируемый видеопоток с веб-камеры пользователя.
Отправка потока в браузере может выглядеть так:
![](https://habrastorage.org/webt/59/e6/22/59e622d673c46479298216.png)
Это пример отправки видеопотока с веб-страницы, открытой в браузере Google Chrome. Поток отправляется на тестовый сервер origin.llcast.com.
После того как Origin принял видеопоток, он ретранслирует принятый поток на два сервера Edge1 и Edge2. Сам Origin-сервер потоки не раздает.
Для упрощения, в данной схеме мы возложили на Origin роль балансировщика нагрузки (LB). Он опрашивает edge-ноды и сообщает подключившимся клиентам-зрителям к какой из нод им следует подключиться.
Балансировка работает так:
![](https://habrastorage.org/webt/59/e6/22/59e622d668bd9596147432.jpeg)
- Браузер обращается к балансировщику (в данном случае совпадает с Origin).
- Балансировщик возвращает один из серверов: Edge1 или Edge2.
- Браузер устанавливает соединение с сервером Edge2 (в примере), по протоколу Websocket.
- Сервер Edge2 отдает видео трафик по WebRTC.
Origin периодически опрашивает серверы Edge1 и Edge2, проверяя их доступность и собирает данные по нагрузке. Этого достаточно чтобы принять решение, на какой из серверов «приземлить» новый коннект. Если один из серверов по какой-то причине недоступен, коннекты будут «приземляться» на оставшийся Edge-сервер.
2) Edge1 и Edge2
Это серверы, которые занимаются непосредственно раздачей аудио и видео трафика зрителям. Каждый из них получает поток от Origin-сервера и каждый предоставляет Origin-серверу информацию о своей загрузке.
3) TURN relay — сервер.
Чтобы обеспечить действительно низкую задержку, вся вышеописанная схема доставки аудио и видео работает по протоколу UDP. Однако данный протокол может быть закрыт на сетевых экранах и иногда приходится пускать весь трафик через HTTPS порт 443, который как правило, открыт. Для этого используется TURN relay. Если нормальное соединение по UDP не проходит, видео пойдет через TURN и по TCP. Это неизбежно ухудшит задержку, но если большая часть ваших пользователей-зрителей находится за корпоративными файрволами, то так или иначе придется использовать TURN для WebRTC либо другой способ доставки контента по TCP, например Media Source Extension / Websocket.
В данном случае, мы выделяем 1 сервер специально для прохождения файрволов. Он будет раздавать видео по WebRTC / TCP для тех зрителей, кому не посчастливилось получить нормальное соединение по UDP.
Географическое распределение
Origin-сервер будет находиться в датацентре Франкфурта. В то время как Edge1 сервер будет находиться в Нью-Йорке, а Edge2 будет расположен в Сингапуре. Таким образом мы получаем распределенную трансляцию, охватывающую большую часть земного шара по протяженности.
![](https://habrastorage.org/webt/59/e6/22/59e622d6d373e642560088.jpeg)
Установка и запуск серверов
Для недорогого тестирования, возьмем виртуальные серверы Digital Ocean.
Заготовим 4 дроплета (виртуальных сервера) на DO, по 2 Gb RAM каждый. Это обойдется нам в 12 центов в час, а одни сутки примерно в 3 доллара.
![](https://habrastorage.org/webt/59/e6/22/59e622d6dc904975507953.jpeg)
В качестве Origin и Edge1, Edge2 — серверов, будет использоваться WebRTC сервер WCS5. Его нужно скачать и установить на каждый из дроплетов.
Процесс установки WCS-сервера выглядит так:
1) Скачиваем дистрибутив.
wget https://flashphoner.com/download-wcs5-server.tar.gz
2) Распаковываем и устанавливаем.
tar -xvzf download-wcs5-server.tar.gz
cd FlashphonerWebCallServer-5.0.2505
./install.sh
Не забываем установить haveged, который может потребоваться для ускорения запуска WCS сервера на Digital Ocean.
yum install epel-release
yum install haveged
haveged chkonfig on
haveged
3) Запускаем.
service webcallserver start
4) Создаем сертификаты Let’sencrypt
wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto
certbot-auto certonly
Certbot положит сертификаты в папку /etc/letsencrypt/live
Пример:
/etc/letsencrypt/live/origin.llcast.com
6) Импортируем сертификаты Let’sencrypt
├── cert.pem
├── chain.pem
├── fullchain.pem
├── fullchain_privkey.pem
├── privkey.pem
└── README
Из этих файлов нам потребуются всего два: цепочка сертификатов и приватный ключ:
- fullchain.pem
- privkey.pem
Загружаем их через Dashboard / Security / Certificates. Чтобы зайти в админку WCS-сервера, открываем адрес origin.llcast.com:8888, где origin.llcast.com — доменное имя вашего WCS сервера.
Результат импорта выглядит так:
![](https://habrastorage.org/webt/59/e6/22/59e622d7194e3898004635.png)
Все готово. Импорт прошел успешно и теперь нужно выполнить короткий тест чтобы убедиться, что все работает. Заходим в Dashboard сервера в демо — пример Two Way Streaming. Нажимаем кнопку Publish и через несколько секунд Play. Убеждаемся что сервер работает, принимает потоки и отдает их на воспроизведение.
![](https://habrastorage.org/webt/59/e6/22/59e622d673c46479298216.png)
После того как проверили один сервер, делаем с остальными двумя (Edge1 и Edge1) тоже самое. Устанавливаем на них WCS5, получаем сертификаты Let's Encrypt и импортируем в установленный WCS-сервер для корректной работы. В завершение каждой установки проводим простой тест, который показывает, что сервер откликается и играет потоки.
В результате мы настроили три сервера, доступные по следующим адресам:
Конфигурация Origin-сервера
Остается сконфигурировать и объединить серверы в CDN. Начинаем с сервера Origin.
Как мы уже писали выше, Origin является в том числе и балансировщиком. Находим в конфигах файл WCS_HOME/conf/loadbalancing.xml и прописываем в нем Edge-ноды. В результате конфиг будет выглядеть так:
<loadbalancer mode="roundrobin" stream_distribution="webrtc">
<node id="1">
<ip>edge1.llcast.com</ip>
<wss>443</wss>
</node>
<node id="2">
<ip>edge2.llcast.com</ip>
<wss>443</wss>
</node>
</loadbalancer>
Данный конфиг описывает следующие настройки:
- На какие ноды должен ретранслироваться видеопоток с Origin (edge1 и edge2).
- По какой технологии будет происходить ретрансляция (webrtc).
- Какой порт нужно использовать для Websocket-соединения с edge-нодой (443).
- Режим балансировки — по кругу (roundrobin).
На текущий момент поддерживается два способа ретрансляции Origin — Edge, которые задаются атрибутом stream_distribution, это:
- webrtc
- rtmp
Выставляем здесь webrtc, т.к. нам требуется низкая задержка. Далее необходимо сказать Origin-серверу, что он будет заниматься балансировкой. Включаем балансировщик в конфиге WCS_HOME/conf/server.properties
load_balancing_enabled =true
Кроме этого, назначим балансировщику порт 443, чтобы подключающиеся клиенты могли получать данные от балансера по стандартному порту HTTPS, опять же для успешного прохождения файрволов. В этом же конфиге server.properties добавляем
https.port=443
Далее выполняем перезагрузку WCS-сервера чтобы применить сделанные изменения.
service webcallserver restart
В результате мы включили балансировщик и настроили его на порт 443 в конфиге server.properties, а также настроили ретрансляции Origin-Edge в конфиге loadbalancing.xml.
Конфигурация Edge-серверов
Edge-серверы не требуют особой конфигурации. Просто принимают поток с Origin-сервера, как если бы пользователь отправлял этот поток с веб камеры напрямую на данный Edge-сервер.
Хотя одну настройку добавить все же придется. Нужно переключить Websocket — порт раздающих видео серверов на 443 для лучшей проходимости через файрволы. Если Edge-серверы будут принимать соединения на порту 443 по HTTPS и TURN будет делать тоже самое, мы получим решение с обходом файрвола, которое поможет увеличить доступность трансляции для корпоративных пользователей за файрволами.
Меняем конфиг WCS_HOME/conf/server.properties и выставляем настройку
wss.port=443
Выполняем перезагрузку WCS-сервера чтобы применить сделанные изменения.
service webcallserver restart
На этом настройка Edge-сервера завершена и можно приступать к тестированию CDN.
Трансляция с веб-камеры на Origin
Пользователи нашей мини-CDN делятся на стримеров и плееров. Стримеры — транслируют видео на CDN, а плееры — зрители, это видео играют. Интерфейс стримера может выглядеть так:
![](https://habrastorage.org/webt/59/e6/22/59e622d7999f4763740294.jpeg)
Самый простой способ протестировать этот интерфейс, открыть демо Two Way Streaming на Origin-сервере.
Пример стримера на Origin-сервере:
https://origin.llcast.com:8888/demo2/two-way-streaming
Для тестирования нужно установить Websocket соединение с сервером по кнопке Connect и далее отправить видеопоток на сервер по кнопке Publish. В результате, если все верно настроено, поток появится на серверах Edge1 и Edge2 и его можно будет с них проиграть.
Данный интерфейс всего лишь демо HTML-страницы на основе клиентского JavaScript API (Web SDK) и его можно кастомизировать и привести к любому дизайну с помощью HTML / CSS.
Воспроизведение потока с серверов Edge1 и Edge2
![](https://habrastorage.org/webt/59/e6/22/59e622d7b532e677016672.png)
Интерфейс плеера можно взять с одного из серверов edge1.llcast.com или edge2.llcast.com — пример Demo / Embed Player
Тестирование можно провести, подключившись напрямую к edge-серверу по этим ссылкам:
Edge1
https://edge1.llcast.com:8888/demo2/embed-player
Edge2
https://edge2.llcast.com:8888/demo2/embed-player
Плеер устанавливает Websocket-соединение с указанным Edge-сервером и забирает с этого сервера видеопоток по WebRTC. Обратите внимание, что на Edge серверах Websocket соединение устанавливается по порту 443, который был сконфигурирован выше для проходимости через файрволы.
Этот плеер можно встроить в сайт в виде iframe либо провести более плотную интеграцию с использованием JavaScript API и разработкой собственного дизайна на HTML / CSS.
Подключение балансировщика нагрузки в плеер
Выше мы убедились в том, что Origin получает видеопоток и реплицирует этот поток на два Edge-сервера. Мы подключились к каждому из Edge-серверов по отдельности и протестировали воспроизведение потока.
Теперь нужно задействовать балансировщик чтобы плеер выбирал Edge-сервер из пула автоматически.
Для этого в JavaScript коде плеера нужно указать адрес балансировщика — lbUrl. Если его указать, то пример Embed Player будет сначала обращаться к балансировщику и брать с него рекомендованный адрес сервера, и уже после этого устанавливать соединение с полученным адресом по протоколу Websocket.
URL балансировщика легко протестировать. Открываем его в браузере:
https://origin.llcast.com/?action=server_list
Результатом будет кусок JSON, в котором балансировщик показывает рекомендуемый Edge-сервер.
{
"server": "edge1.llcast.com",
"flash": "1935",
"ws": "8080",
"wss": "443"
}
В данном случае балансировщик вернул первый сервер edge1.llcast.com и показал, что с сервером можно установить защищенное Websocket соединение (wss) по порту 443.
Если обновлять страницу в браузере, балансировщик будет отдавать то один Edge-сервер, то другой, по кругу. Так работает roundrobin.
Осталось включить балансировщик в плеере. Для этого в коде плеера player.js добавляем параметр lbUrl при установке сессии с сервером:
Flashphoner.createSession({urlServer: urlServer, mediaOptions: mediaOptions, lbUrl:'https://origin.llcast.com/?action=server_list'})
Полный код плеера приведен по ссылке ниже:
https://edge1.llcast.com:8888/client2/examples/demo/streaming/embed_player/player.js
Проверяем работу балансировщика и убеждаемся в том, что он корректно раскидывает соединения по нодам edge1.llcast.com и edge2.llcast.com. Для этого просто обновляем страницу плеера и смотрим в Developer Tools вкладку Network, где виден коннект к балансировщику и к серверу edge1.llcast.com (выделено красным).
https://edge1.llcast.com:8888/client2/examples/demo/streaming/embed_player/player.html
![](https://habrastorage.org/webt/59/e6/22/59e622d7d91de553021704.jpeg)
Финальный тест
Мы настроили Orign-Edge, ретрансляции, балансировку и подключение плеера на основе данных балансировщика. Осталось собрать все воедино и провести тест, демонстрирующий работу получившейся CDN.
1. Открываем страницу стримера
https://origin.llcast.com:8888/client2/examples/demo/streaming/two_way_streaming/two_way_streaming.html
Отправляем видеопоток с веб-камеры на Origin сервер, под именем stream1 из браузера Google Chrome.
![](https://habrastorage.org/webt/59/e6/22/59e622d812120137511852.jpeg)
Заходим в Dev Tools — Network и убеждаемся, что транслирующий браузер подключился к Origin-серверу и отправил на него поток.
![](https://habrastorage.org/webt/59/e6/22/59e622d83cdab355309175.jpeg)
2. Открываем страницу плеера на Edge-сервере и играем поток stream1.
https://edge1.llcast.com:8888/client2/examples/demo/streaming/embed_player/sample.html
![](https://habrastorage.org/webt/59/e6/22/59e622d876b0c936203010.png)
Видим, что балансировщик выдал edge1.llcast.com и поток в данный момент забирается с него.
![](https://habrastorage.org/webt/59/e6/22/59e622d88d042339204054.jpeg)
Далее обновляем страницу еще раз и балансировщик подключает плеер к серверу edge2.llcast.com, который находится в Сингапуре.
![](https://habrastorage.org/webt/59/e6/22/59e622d8bd54f668748509.jpeg)
Таким образом мы справились с первой задачей и завершили настройку географически распределенной CDN на базе WebRTC с низкой задержкой.
Далее нужно проверить задержку и убедиться, что она является действительно невысокой относительно RTT. Хорошей задержкой в глобальной сети является стабильная, не превышающая 1 секунды задержка. Проверим что получилось у нас.
Измеряем задержку
Предполагаем что значение задержки будет отличаться в зависимости от того, к какому серверу мы подключились плеером — на Edge1 в Нью Йорке или на Edge2 в Сингапуре.
Для того чтобы иметь какую-то точку отсчета, составим карту RTT, чтобы понимать как устроена наша тестовая сеть и чего ждать от ее участников. Предполагаем, что особых потерь нет и измеряем только время пинга (RTT).
![](https://habrastorage.org/webt/59/e6/22/59e622d8e711b993186081.jpeg)
Как видите, наша тестовая сеть совсем глобальная и неоднородная по задержке. Стример и плеер будут находиться в Новосибирске. Пинг до сервера Edge1 в Нью Йорке составит 170 ms, а до Edge2 в Сингапуре 306 ms.
Будем учитывать эти данные при анализе результатов тестирования. Например понятно, что если мы будем транслировать поток на Origin во Франкфурте, передавать его на Edge 2 в Сингапуре и забирать из Новосибирска, то задержка ожидается никак не меньше (90 + 183 + 306) / 2 = 290 миллисекунд.
Проверим предположения на практике. Для получения данных по задержке, воспользуемся тестом с миллисекундным таймером отсюда, используя его как виртуальную камеру.
Отправляем поток на Origin и делаем несколько скриншотов при воспроизведении с
Edge1 — сервера. Слева расположено локальное видео с камеры с нулевой задержкой, а справа видео, которое пришло на плеер через CDN.
Результаты тестов:
Test number |
Latency (ms) |
1 |
386 |
2 |
325 |
3 |
384 |
4 |
387 |
Average latency: 370 ms |
![](https://habrastorage.org/webt/59/e6/22/59e622d8f3a20975665316.png)
Как видите, средняя задержка видеопотока по пути доставки контента Новосибирск (стример) — Франкфурт (Origin) — Нью Йорк (Edge1) — Новосибирск (плеер), составила 370 миллисекунд. RTT по этому же пути = 90 + 84 + 170 = 344 миллисекунды. Таким образом получаем результат 0.9 RTT, что очень и очень неплохо для доставки живого видеопотока, учитывая, что идеальным результатом был бы 0.5 RTT.
![](https://habrastorage.org/webt/59/e6/22/59e622d914cc3939506257.png)
Задержка и RTT для маршрута Edge1, Нью Йорк
Теперь заберем тот же поток с сервера Edge2 в Сингапуре и поймем как повышение RTT влияет на задержку. Делаем аналогичный тест с сервером Edge2 и приходим к следующим результатам:
Test number |
Latency (ms) |
1 |
436 |
2 |
448 |
3 |
449 |
Average latency: 444 ms |
Из этого теста видим, что соотношение задержки к RTT около 0.7, т.е лучше, чем в предыдущем тесте.
![](https://habrastorage.org/webt/59/e6/22/59e622d92e887730500613.png)
Задержка и RTT для маршрута Edge2, Сингапур
Результаты тестов дают основание предполагать, что с ростом RTT, значение задержки не будет резко расти, а будет плавно приближаться к 0.5 RTT.
Так или иначе, цель построения географически распределенной CDN с задержкой менее секунды достигнута и есть поле для будущих тестов и оптимизаций.
В заключение
В итоге, мы развернули три сервера: Origin, Edge1 и Edge2, а не четыре, как обещали в начале статьи. Настройка, конфигурация и тестирование TURN сервера за файрволом сильно увеличивают и без того раздутый материал. В связи с этим полагаем, что есть смысл оформить его в отдельной статьей. Пока же просто не забываем, что четвертым сервером в этой схеме может быть TURN-сервер, который обслуживает WebRTC браузеры по порту 443, обеспечивая проходимость через файрволы.
Итак, CDN для WebRTC потоков настроена и задержка измерена. Финальная схема работы вышла такой:
![](https://habrastorage.org/webt/59/e6/22/59e622d98be84990259553.png)
Как можно эту схему улучшить и расширить? Можно улучшать в ширину. Например добавляем несколько Origin-серверов и ставим балансировщик между ними. Таким образом мы будем балансировать не только зрителей, но и стримеров — тех, кто непосредственно транслирует контент с веб-камер:
![](https://habrastorage.org/webt/59/e6/25/59e6250de534b559634663.png)
CDN пока открыта в демо-доступе на домене llcast.com. Возможно скоро заморозим ее, и демо ссылки работать перестанут. Поэтому если появится желание измерить задержку — добро пожаловать. Хорошего стриминга!
Ссылки
https://origin.llcast.com:8888 — демо сервер Origin
https://edge1.llcast.com:8888 — демо сервер Edge1
https://edge2.llcast.com:8888 — демо сервер Edge2
Two Way Streaming — демо интерфейс трансляции потока с веб камеры на Origin.
Embed Player — демо интерфейс плеера с балансировщиком между Edge1 и Edge2
WCS5 — WebRTC сервер, на основе которого построена CDN