Pull to refresh

Онлайн-концерт в облаках

Reading time6 min
Views3.9K
Ресурс Appleinsider.ru запускает новый, не имеющий аналогов в российском интернет-сегменте проект по онлайн-трансляции живых выступлений как молодых музыкальных коллективов, так и именитых музыкантов. Периодичность мероприятия — около 2-х трансляций в месяц.

Первопроходцами 6 декабря стали Федор Чистяков & F4BAND (экс-солист группы НОЛЬ). Во время прямого эфира было около 300 человек, которые смогли оценить чистый студийный звук и атмосферу живого концерта.

Для этой трансляции компания Clodo выделила нам свои мощности и канал шириной 500 мегабит/секунду. Для довольно качественного битрейта потока в 128 килобит/сек это означало 4000 клиентов «в вакууме». Мы предполагали, что придет до 2000 слушателей и перестраховались, однако на этот раз не удалось достичь договоренности с партнерами и наши оценки аудитории оказались сильно заниженными. Попытка привести заинтересованный народ с хабра закончилась баном хабратоварища Dudlik.

Тем не менее, нам довольно успешно удалось провести концерт. Вкратце о технической реализации — мы использовали программу Icecast2 на нескольких серверах, с балансировкой нагрузки с помощью самописного bash-скрипта. Подробнее под катом.

Техническая реализация


Чтобы воспользоваться возможностями хостинга, нам нужно было создать 5 инстансов, каждому из которых выделялось по 100 мегабит/сек. Общая схема взаимодействия системы выглядела так:


У нас имеется первый сервер, на который транслировался поток-источник (ведущие и музыканты), а тот уже занимался:
1) собственно трансляцией потока для клиентов;
2) ретрансляцией (relay) на другие сервера.

Для раздачи аудиопотока мы использовали Icecast2 из репозитория Ubuntu 10.04. Вкратце, вот список пакетов, которые пришлось установить на сервера трансляции:
g++ libmp3lame-dev libshout3-dev icecast2 libperl-dev libmp3-info-perl

Когда поток-источник отсутствует, на первом сервере в эфир идет ротация музыки из определенного каталога. Формируется nonstop-поток с помощью Ices 0.4, который мы собрали из исходников:
wget http://downloads.us.xiph.org/releases/ices/ices-0.4.tar.gz
tar -zxvf ices-0.4.tar.gz
cd ices-0.4
./configure --with-perl
make
make install
Музыка хранится в каталоге /home/ftp, а ID3-теги из них вытаскивались с помощью Perl-модуля, взятого отсюда (для этого мы собирали ices с ключом --with-perl). Конфиг /usr/local/etc/ices.conf:
<?xml version="1.0"?>
<ices:Configuration xmlns:ices="http://www.icecast.org/projects/ices">
  <Playlist>
    <Randomize>1</Randomize>
    <Type>perl</Type>
    <Module>ices</Module>
    <Crossfade>5</Crossfade>
  </Playlist>

  <Execution>
    <Background>0</Background>
    <Verbose>0</Verbose>
    <BaseDirectory>/tmp</BaseDirectory>
  </Execution>

  <Stream>
    <Server>
      <Hostname>air1.appleinsider.ru</Hostname>
      <Port>1976</Port>
      <Password>SOURCEPASSWORD</Password>
      <Protocol>http</Protocol>
    </Server>

    <Mountpoint>/nonstop</Mountpoint>
    <Name>AppleInsider.ru Radio</Name>
    <Genre>not specified</Genre>
    <Description>Music from our listeners</Description>
    <URL>http://www.appleinsider.ru/ipodcast/</URL>
    <Public>1</Public>

    <Bitrate>128</Bitrate>
    <Reencode>1</Reencode>
    <Samplerate>44100</Samplerate>
    <Channels>2</Channels>
  </Stream>
</ices:Configuration>

Вот наиболее интересные места конфига первого сервера (секции paths, logging, security я опустил), файл /etc/icecast2/icecast.xml:
<icecast>
    <limits>
        <clients>1000</clients>
        <sources>6</sources>
        <queue-size>524288</queue-size>
        <client-timeout>30</client-timeout>
        <header-timeout>15</header-timeout>
        <source-timeout>10</source-timeout>
        <burst-on-connect>1</burst-on-connect>
        <burst-size>65535</burst-size>
    </limits>

    <authentication>
        <source-password>SOURCEPASSWORD</source-password>
        <relay-password>RELAYPASSWORD</relay-password>
        <admin-user>admin</admin-user>
        <admin-password>ADMINPASSWORD</admin-password>
    </authentication>

    <hostname>air1.appleinsider.ru</hostname>
    <listen-socket>
        <port>1976</port>
    </listen-socket>

    <mount>
        <mount-name>/nonstop</mount-name>
        <charset>UTF8</charset>
    </mount>

    <mount>
	<mount-name>/air</mount-name>
	<max-listeners>600</max-listeners>
	<charset>UTF8</charset>
	<fallback-mount>/nonstop</fallback-mount>
	<fallback-override>1</fallback-override>
    </mount>

. . .
</icecast>

Конфиг второго и последующих серверов, обращаем особое внимание на секции relay и mount:
<icecast>
    <limits>
        <clients>1000</clients>
        <sources>6</sources>
        <queue-size>524288</queue-size>
        <client-timeout>30</client-timeout>
        <header-timeout>15</header-timeout>
        <source-timeout>10</source-timeout>
        <burst-on-connect>1</burst-on-connect>
        <burst-size>65535</burst-size>
    </limits>

    <authentication>
        <relay-password>RELAYPASSWORD</relay-password>
        <admin-user>admin</admin-user>
        <admin-password>ADMINPASSWORD</admin-password>
    </authentication>

    <hostname>air2.appleinsider.ru</hostname>
    <listen-socket>
        <port>1976</port>
    </listen-socket>

    <relay>
	<server>air1.appleinsider.ru</server>
	<port>1976</port>
	<mount>/air</mount>
	<local-mount>/air</local-mount>
	<username>relay</username>
	<password>RELAYPASSWORD</password>
	<on-demand>0</on-demand>
    </relay>

    <mount>
	<mount-name>/air</mount-name>
	<max-listeners>600</max-listeners>
	<charset>UTF8</charset>
    </mount>

. . .
</icecast>

Таким образом, при старте icecast2 на втором и следующих серверах они автоматически подключаются к точке монтирования /air первого сервера, копируют его «на лету» и начинают раздавать в своей одноименной точке монтирования /air. На каждый сервер мы ввели ограничение в 600 слушателей, т.е. в идеальном случае канал каждого сервера задействовался бы на 75%.

Распределение нагрузки


Теперь о том, как мы равномерно распределяли нагрузку (хотя, как оказалось, это и не потребовалось).

Слушатели могли попасть на онлайн-концерт двумя способами: либо через веб-плеер, открыв страничку podcast.appleinsider.ru и нажав на кнопку Play, либо скопировав ссылку вида air.appleinsider.ru/apple.m3u на той же странице и «скормив» ее своему любимому десктопному/мобильному плееру.

В первом случае балансировка осуществлялась случайно — когда клиент открывал страничку с веб-плеером (кстати, он у нас построен на jPlayer), то с помощью Javascript генерировалось случайное число от 1 до 5, и поток брался с соответствующего сервера (air1-air5).

Во втором случае, т.е. когда пользователь отдавал M3U-ссылку своему плееру, происходило следующее. Вообще, когда у icecast2 запрашивают ссылку вида server/mount.m3u, он сначала ищет файл /usr/share/icecast2/web/mount.m3u, и если находит, отдает его. Нам оставалось лишь правильно формировать этот файлик.

Мы попросту раз в минуту по крону опрашивали нагрузку каждого из серверов, обновляли суммарную статистику и заново перенаправляли символьную ссылку apple.m3u на один из заранее заготовленных файлов, к примеру для первого сервера, этот файл содержал:
http://air1.appleinsider.ru:1976/air


Вот собственно bash-скрипт:
#!/bin/sh

TOTAL=0
IMIN=1
MIN=100

# Собираем нагрузку (т.е. количество слушателей) каждого сервера
# Для этого опрашиваем служебную страничку и парсим ее
for NUM in `seq 1 5`; do
	DATA=`curl --silent http://air$NUM.appleinsider.ru:1976/status2.xsl`
	AIR=`echo $DATA | sed 's/.*\/air//' | cut -d"," -f4`
	NONSTOP=`echo $DATA | sed 's/.*\/nonstop//' | cut -d"," -f4`
	if [ "x${AIR}" = "xCurrent Listeners" ]; then
		AIR=0
	fi
	if [ "x${NONSTOP}" = "xCurrent Listeners" ]; then
		NONSTOP=0
		echo "air$NUM : $AIR"
	else
		if [ "x${NONSTOP}" = "x0" ]; then
			echo "air$NUM : $AIR"
		else
			echo "air$NUM : $AIR / $NONSTOP"
		fi
	fi

	# NOW - нагрузка сервера под номером NUM
	NOW=`expr $AIR + $NONSTOP`
	
	# TOTAL - суммарная нагрузка
	TOTAL=`expr $TOTAL + $NOW`

	# Определяем номер наименее нагруженного сервера
	if [ $NOW -lt $MIN ]; then
		MIN=$NOW
		IMIN=$NUM
	fi
done
echo "-------------------"
echo "Total: $TOTAL"

# Обновляем статистику на веб-страничке трансляции
echo $TOTAL > stat.txt
scp stat.txt podcast:/var/www

# Обновляем M3U-файл
echo air.m3u --\> http://air$IMIN.appleinsider.ru:1976/air
ssh air1 ln -sf /usr/share/icecast2/web/air.m3u_$IMIN /usr/share/icecast2/web/air.m3u

echo "Server $IMIN is on duty."

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

Итоги


Вот график объема аудитории во время онлайн-трансляции, 280 человек в пике:


Подписаться на подкаст и скачать запись онлайн-концерта можно тут: RSS | MP3 | iTunes

Следующее мероприятие планируется на 25 декабря. О его участниках пока говорить не будем, т.к. переговоры находятся в самой активной стадии. Все подробности и анонсы можно будет узнать на Appleinsider.ru
Tags:
Hubs:
+23
Comments12

Articles

Change theme settings