понедельник, 28 января 2013 г.

Разработка видеохостинга на Erlang

Представляем вашему вниманию доклад Максима Лапшина, сделанный им на конференции Application Developer Days. Мы собрали воедино видео и аудио, слайды презентации, а также стенограмму доклада. Последнее потребовало огромных усилий, но оно явно того стоит. Сорокаминутный доклад можно «услышать» в несколько раз быстрее.

Свел видео и презентацию в единый ролик, а также записал стенограмму Стас Фомин (человек и пароходлокомотив :)).

Аннотация


Максим Лапшин (erlyvideo), разработчик масштабируемых веб-сервисов, рассказал о разработке сервера видеостриминга на Erlang. Речь идет об open-source проекте ErlyVideo — набирающем популярность надежном, масштабируемом и бесплатном сервере для трансляции любого видео — от охранных камер до видеоконференций. Особый интерес представляет именно технология, ведь именно выбор такого малоизвестного языка как Erlang, обеспечил высокую надежность, масштабируемость и скорость разработки.

Erlang — надежный объектный язык для создания сетевых сервисов. Принятые в нём концепции процессов и немутабельности данных, делают его единственной платформой в которой одновременно существует и сборка мусора, и фиксированное время смерти объекта. Семантика языка одна из самых простейших среди распространенных на рынке.

Эти особенности делают Erlang прекрасным выбором для обслуживания statefull клиентов: видеостриминговый сервер (erlyvideo), самый распространенный jabber-сервер (ejabberd), покерные серверы (OpenPoker) и т.п. В докладе рассмотрено, почему же именно на Erlang такое делать очень удобно.

Видео




Подкаст


Ссылка на подкаст.

Презентация


Ссылка на PDF со слайдами.

Стенограмма


Стенограмму по видеозаписи записал Стас Фомин.

Что такое стриминг?

Добрый день, меня зовут Максим Лапшин, я автор видеостримингового сервера ErlyVideo, написанного наErlang, и сегодня я бы хотел вам рассказать, что это за продукт, зачем он был сделан, … что это такое, зачем я все это сделал.

Итак, что такое стриминг вообще.
Слайд:
Ютуб — это не стриминг
Что такое YouTube? Там хранится огромное количество разного видео, но это не стриминговый проект, там нет стриминга. Там просто десятисекундные ролики, которые выдаются вам nginx-oм, ну или другим вебсервером.

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

Пользовательское ТВ
Слайд:
Пользователь загружает видеофайлы
  • Составляет плейлист
  • По запросу других плейлист начинает проигрываться
  • Если никому не нужно, то видео не играется
Давайте рассмотрим задачу пользовательского телевидения, которая у меня недавно стояла. Значит, идея в чем — пользователь загружает свои файлы, почти как в ютуб, потом формирует из них плейлист, который он хочет, чтобы показывался как телепрограмма на обычном телевидении.

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

Почему же не сделать это все на обычном nginxe?

Вроде как обкатанная технология, у того же ютуба работает, но нет, не работает.

Что делает стример?
Слайд:
Что делает стример?
  • Распаковывает видео и аудио из файловых контейнеров
  • Упаковывает в транспортный контейнер
  • Посылает кадры синхронно с реальным временем
Проблема вся в том, что необходимо имеющиеся файлы отдавать в видеопоток. Потому что необходимо монотонно всем пользователям показывать одно и тоже.

Для этой задачи как раз нужен стример, то есть сервер потокового видео, который сделает вот что.
  1. Он заберет нужные файлы, из тех контейнеров, в которых они лежат у вас на диске.
  2. Упакует их в транспортный контейнер, по которому можно будет доставить видео тем пользователям, которые пришли на него посмотреть.
  3. И очень важно понимать, что он посылает кадры синхронно с реальным временем.
Значит, если вы зашли, вы за полчаса получаете реального времени по часам вы получите полчаса реального времени, которые из файла. Если вы просто скачиваете файл, вы бы могли гораздо быстрее скачать.

Отступление про кодеки

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

Контейнер — это то, во что упаковываются уже закодированные данные. Например, если вы встретили h264 или AAC, то это видео и аудиокодеки, соответственно. А MP4 … — нет такого кодека, это контейнер, в который можно убрать абсолютно любое видео и абсолютно любой звук.
Слайд:
  • Кодек — формат представления сжатых аудио и видео данных
  • Контейнер — формат упаковки одного и более потоков аудио и видео в файле или в потоке
  • H.264/AAC — лучшие кодеки
  • MP4 — самый компактный файловый контейнер
Этапы User TV
Слайд:
Этапы User TV
  • Скачать плейлист
  • Распаковать файл
  • Упаковать кадры в транспортный контейнер (RTMP, MPEG-TS,…)
  • Зачистить всe, когда уйдут клиенты
  • Позволить обновить код, не отключая клиентов
Итак, чем же занимается сервер, показывая пользовательское телевидение? Он скачивает плейлисты, распаковывает файлы, перепаковывает их, зачищает все, и очень важная вещь, про которую я отдельно не упомянул, это то, что в этой задаче очень важно обновлять код, не отключая клиентов, потому что бывает до тысячи, … многих тысяч клиентов, которые пришли посмотреть интересное видео. Если мы захотим в этот момент выкатить апдейт под них, то эти тыщи клиентов опять к нам придут, реконнектится.

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

Традиционные способы решения

На чем это обычно люди делают, такие решения? Традиционные способы решения, это традиционные инструменты — Java, С++, на них есть продукты, которые занимаются потоковым видео. Это например, Red5, бесплатный, платная Wowza, написанная на Java, либо это rtmpd, написанный на C++.

Парсинг mp3 на Java

В чем проблема…? Ну вот пример я привел, это маленький кусочек кода, как парсится RTMP на Java, это маленький кусочек кода, одна сотая файла.

Вот так выглят любой сервер на Java, можно присматриваться — это малая, малая, часть файла. Вникнуть в это очень сложно.
if (id3v1 instanceof ID3V1_1Tag) {
   try {
     // Add the track property
     graph.add(mp3Resource, processor.resolveIdentifier(IdentifierProcessor.TRCK),
         factory.createLiteral("" + ((ID3V1_1Tag) id3v1).getAlbumTrack()));
   } catch (GraphException graphException) {
     throw new ParserException(
         "Unable to add track number to id3v1 resource.",
         graphException);
   } catch (GraphElementFactoryException graphElementFactoryException) {
     throw new ParserException(
         .... ещѐ 600 строк кода
         graphElementFactoryException);
   }
 }

Парсинг mp3 на Erlang

Это все, что нужно написать на Erlange, чтобы декодировать MP3. Все. Пять строчек. Оно уже все распаковано и можно отправлять пользователям.

decode(<<2#11111111111:11, VsnBits:2, LayerBits:2, _:1, BitRate:4, _/binary>> = Packet) ->
  Layer = layer(LayerBits),
  Version = version(VsnBits),
  <<Frame:(framelength(bitrate({Version,Layer}, BitRate))/binary, Rest/binary>> = Packet,
  {ok, Frame, Rest}.

Соответственно, что мы получаем — уже начиная с самого начала, с распаковки файла, что-то не то, и в Java, и в C++, очень много кода, очень много накладной логики мы пишем в нашем коде.

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

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

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

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

И другая проблема, что когда у вас начинаются тысячи клиентов, вам необходимо сложно организовывать ввод-вывод. Вам недостаточно просто использовать треды, и просто писать в сокет, вам необходимо использовать сложные библиотеки либо eventы, которые используют разные сложные механизмы.
Слайд:
Проблемы классических решений при тысячах клиентов
  • Управление памятью: утекание, либо преждевременное высвобождение
  • Контроль за ресурсами клиентов
  • Хаотическое разрушение системы при сбое в одном месте
  • Ввод/вывод при обслуживании тысяч клиентов

Что же получается? Сервер Red5 валится под сотней пользователей. Эх, тут к сожалению, не красная получилось
image
Уже на ста пользователях сервер валится и не обслуживает клиентов. Почему? Да потому, что он написан плохо, при его разработке люди не учитывали, вопрос ввода-вывода, и вот, он просто перестает обслуживать.

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

Его данные остались навсегда, мы не можем получить информацию, что надо отключить. И все, до рестарта сервера.
Слайд:
epoll/kqueue сложны для долгих соединений из-за управления памятью.
Что же касается ввода-вывода, то механизмы epoll/kqueue, для которых есть библиотеки libevent, это единственный способ обслуживать тысячи сокетов, они очень, очень сложны, для … когда у вас начинается сложная бизнес-логика…, потому что эффективное управление памятью в event-ной модели, по-моему, безумно сложно.
image
Вот, получается такая конструкция с С++ным сервером. Вы гарантированно начинаете ваш рабочий день с того, что вы выгребаете core-ки, сохранившиеся за ночь на файловой системе и хорошо, если вам хватило жесткого диска.

Корни проблем

В чем-то корни этих проблем которые кроются, общие для традиционных решений. Во-первых, это общая память.
image
Вот, к сожалению, картинка опять не видна.

Общая память, которая разделяется между всеми объектами, которые есть в системе. Кто угодно может пойти куда угодно, взять ссылку на что угодно, и в итоге, получается вот такая конструкция, когда все объекты перекрестно друг на друга ссылаются, и нам гораздо сложнее в этой ситуации контролировать память, кто и что захватил, и кому что нужно.
image
Надо понимать, что, эти проблемы нас не интересуют, когда мы пишем сайт на PHP. Они нас не интересуют, по той причине, что ваше приложение, которое работает при обслуживании веб-сервиса, живет одну секунду максимум. Через одну секунду, все, что оно использовало, можно уничтожить, потому, что это все становится ненужным, у нас уже новый запрос, новое соединение.
Слайд:
Web-подход → «пускай течет, скоро прибьем» → не работает.
Здесь такого не будет, клиенты подключенные часами, днями и даже больше. И необходимо, чтобы ваш код код работал эффективно, не утекая, неделями.

Erlang решает эти проблемы радикально

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

Это было сделано на 90 % из-за его концепции процессов.
Слайд:
Процессы
  • Параллельные потоки выполнения
  • Изолированная область памяти
  • Обмен через посылку сообщений
  • Переменные неизменяемые
  • Нет данных вне процессов
Процессы в Erlange, это что-то типа тредов, в обычных системах. Они легковесны, они гораздо меньше места занимают, и самое важное — они абсолютно изолированы.

Каждый процесс в Erlange, это такая коробочка, из которой ничего наружу не вытекает. И мы точно знаем, что вся память, которая есть в системе, гарантированно принадлежит какому-то процессу. Не может быть данных вне процесса. То есть всегда, если есть какой-то гигабайтный кусок памяти, вроде бы ничейный, но мы знаем, что за процесс им владеет и можем его прибить, чтобы эти данные освободить.
Вопрос: А ведь бинари по ссылке передается между процессами?
Ну это тонкости реализации, а на самом деле эти бинари можем отследить всегда. Они исследуются.
Вопрос: А чем и как?
Мы знаем, процессу известно, на какие бинари и какого размера он ссылается.

Поэтому, что у нас получается: все данные, которые есть в системе, хранятся исключительно внутри перечислимых процессов. Можно перебрать все процессы в системе, чтобы узнать, кто сожрал всю память, и прекратить это издевательство над системой.
Слайд:
Все данные хранятся внутри перечислимых объектов
Следующая особенность процессного подхода к организации данных внутри и потоков выполнения, заключается в том, что ошибки которые возникают, жестко ??? процесса. Если у нас есть ошибка, которую мы не обработали, которую мы не захотели ее перехватывать, мы решили, пуская она сыпется дальше, нас не интересует ее судьба, это фатальная ошибка, наш процесс завершается. И что самое главное, это очень-очень похоже на освобождение, уничтожение объекта, потому что это известная по времени процедура, то есть мы знаем, что раз ошибка возникла, то процесс все, прекратился. Это будет не через час, не через два дня, это будет прямо сейчас. И важно понимать, что процессы, которые заказали…, которые хотят следить за его состоянием, другие процессы, соседние, они получат об этом информацию, что их сосед умер.
Слайд:
Обработка ошибок
  • Их можно ловить
  • Если не ловить, то завершается процесс
  • Соседи об этом узнают через сообщения
  • Гарантированная зачистка ресурсов
В итоге получается, что у нас можно следить за состоянием процессов. Например, мы запускаем отдельно процесс, который обслуживает соединения с пользователем, начинаем за ним следить, и если там была какая-нибудь ошибка, наша ошибка в коде, скорее всего, то процесс, который за ним следит, узнает, «ага, у нас умер процесс обслуживающий сокет». Значит, в принципе, нет дальше смысла обслуживать пользователя, все равно уже нечем его обслуживать, и надо каскадно завершить все те процессы, которые были созданы для его обслуживания.

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

Зачем это нужно? Например у вас есть один из самых важных процессов в системе, это демон, это процесс, который слушает сокет. Он закрепился на сокет, и принимает соединения от системы. Вы можете быть уверены, что он то будет работать гарантировано, иначе вся ваша система вся может отвалится (???). Вот если он отвалился, уже нет смысла ваш сервер терзать.
Слайд:
Слежение за процессами
  • Связи
  • Супервизоры
  • appmon
К сожалению, я не смогу показать на своем ноутбуке, у меня нет переходника, но я хотел бы показть такую вещь, как app monitor. Это поставляющися, опять таки с платформой механизм, который позволяет вам графически, посмотреть список всех процессов, которые есть у вас, с их деревом. То есть мы можем…, это очень полезная вещь, когда вы можете видеть, что к вам приходит пользователь, у вас создался объект под него, он запросил какие-то ресурсы, под них просто залез (???) в какие-то процессы… Пользователь уходит, процессы остаются — фактически это утечка процессов, а с помощью app monitora, все это видится очень наглядно.

Но, к сожалению, не покажу вам. :(

В Erlang настоящее горячее обновление кода

А еще в эрланге есть, наверное единственное из всех существующих платформ, настоящее, горячее обновление кода. Выглядит это так — клиенты не отключаются, они продолжают работать, в случае с видеостримером, они продолжают получать видео, в случае с онлайн-играми не теряется соединение, а код уже обслуживает новый.
Слайд:
Без отключения клиентов!
Других систем, которые позволяют такое делать я не знаю.

Какие результаты использования Erlang?

И что же в итоге получилось, после того, как я решил воспользоваться эрлангом для создания своего сервера? Получился наш видеостриминговый серве Erlyvideo, которые сейчас находится в двойке-тройке лучших, в своей области, по набору реализованных фич, по скорости развития, по стабильности и эффективности.
Слайд:
Erlyvideo:
  • Мультипротокольный сервер
  • Держит тысячи клиентов на одном сервере
  • Существующая инфраструктура для плагинов
Например, совершенно нормально обслуживает тысячи клиентов с одного сервера, сейчас стоит в продакшне у BD (???) и работает.

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

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

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

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

Выводы

Слайд:
Задачи потокового видео имеют специфику, отличающую их от веба:
  • Необходимы инструменты эффективные и высокоуровневые одновременно
  • Erlang прекрасно вписывается в эту нишу
  • Практическое использование показало эффективность выбора
В итоге, какие же выводы можно сделать из применения Erlanga для этой задачи?

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

У вас не будет этих проблем, потому что… просто в силу специфики организации кода.

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

Применимость erlang

Ну понятно, что ниша потокового видео это достаточно узкая вещь, этих продуктов всего штук пять на рынке, и в принципе, этого наверное достаточно. Нет смысла писать другой сервер потокового видео на эрланге.
Слайд:
  • Видеостриминг (erlyvideo)
  • Jabber-сервер (ejabberd)
  • Банковский процессинг (Приват Банк)
  • Онлайн игры (Online Poker)
Однако, у него есть другие ниши применимости, например, на том же эрланге сделан самый лучший сервер Jabbera, это ejabberd, он настолько крут, что вот в яндексе, например, несмотря на жуткую антипатию к эрлангу, решили применять его, да, Яш? Сильно Гриша не любит Эрланг (bobuk)? Он очень ругался, плевался, но выбора у них не осталось — это говорит очень многое о продукте. Также, например, достоверно известно, банки делают системы банковского процессинга. Я не знаю, конечно, какие именно детали, детали конечно не раскрываются, но я знаю, что у того же Приват-банка, есть еще ряд компаний, переводящих свои системы долгоживущего процессинга на эрланг, потому что им оказывается это удобным.

Ну и конечно, онлайн-игры. Ко мне регулярно обращаются люди, после нашего успеха с раскручиванием топовой вконтактовской игрушки, то есть обращаются люди «нам бы сделать, чтобы у нас работало хорошо и удобно».

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

И вот даже есть онлайн-реализация покера на эрланге.

Вопросы?

Так что, в целом, у меня наверно все, вот такой вот доклад получился. Если у вас есть какие-нибудь вопросы, я с радостью на них отвечу.
Слайд:


Сессии ответов и вопросов не влезла в размер хабропубликации, поэтому ее надо искать на сайте конференции по ссылке «стенограмма».

Комментариев нет:

Отправить комментарий