Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Платформа для видеосервиса сроком в квартал

Anna | 1.06.2014 | нет комментариев
Сегодня мы расскажем, как нам удалось возвести свою платформу для обслуживания видео на Одноклассниках на Java за 3 месяца.

Начнем с того, что представляет собой видеосервис на Одноклассниках. Он доступен как на вебе, так и в версиях для мобильных устройств. Одним из различий Одноклассников от других соцсетей является присутствие видеовитрины, где в разделах «ТОП недели», «Новинки» и каналах собраны видео. Для этих разделов видео отбирается механически по хитроумному алгорифму на основании числа просмотров, классов и скорости роста популярности видео. И безусловно, на витрине представлены каналы с контентом от партнеров:— сериалы, ТВ шоу, мультики и кино.

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

Видеосервисом на Одноклассниках пользуется свыше 10 миллионов уникальных пользователей в день, которые глядят свыше 70 миллионов видеороликов и загружает 50 тысяч видео в день.

Видеобаза Одноклассников насчитывает свыше 28 миллионов роликов. Исходящий трафик по вечерам достигает 80 гигабит в секунду. Каждодневно загружаемые 5 терабайт нового видео в день преобразуются в наш внутренний формат и на выходе получается 2 терабайта. Получившиеся файлы хранятся в 3 копиях, что, в результате, суммарно составляет 6 терабайт нового видео в день. Входящий трафик по загрузке достигает 2 гигабит в пиковые часы.



На чем это все работает? Теперь парк видеосервиса насчитывает порядка 200 серверов, и скоро их будет гораздо огромнее. Хранилище занимает 70 серверов, суммарный объем 5 Петабайт. 30 серверов занимаются хранением метаданных, это база данных и кэши перед ними. Еще 60 серверов занимаются трансформацией пользовательского видео в наши форматы. Еще 30 серверов — для загрузки и раздачи.

Видеосервис на Одноклассниках мы начали разрабатывать еще в 2010 году. Вначале у нас не было своей платформы и мы разрешали пользователям только делиться ссылками на контент, тот, что располагался на других источниках: Rutube, YouTube, Видео Mail.Ru. В 2011 году мы запустили загрузку видео через портал Одноклассники, но все это работало на базе платформы Видео Mail.Ru. Сервис стал расти, стали предъявляться новые требования, и в январе 2013 года мы осознали, что без создания своей видеоплатформы наш сервис прогрессировать дальше не может.

Новая платформа — новые требования. Первое неотделимое требование, которое предъявляется ко каждому серверам Одноклассников — это безопасность, отказоустойчивость и полное сохранение функциональности в случае потери сервера либо дата-центра. Дальше идёт возобновляемая загрузка. Она нужна, напротив мы будем терять пользовательский контент. На третьем месте воспроизведение видео с всякого момента без заблаговременной буферизации каждого видео целиком. Все это нам хотелось видеть и на мобильных устройствах. Также мы дюже любим и умеем эксплуатировать сервисы на Java, следственно она также была добавлена к списку требований.

Посмотрим, как выглядит каждый видеоконвейер изнутри. Пользователь загружает видео на upload-сервер (его мы будем обозначать на картинках стрелочкой вверх). Дальше видео попадает в серверное хранилище, а после этого в сервер трансформации. На нём мы преобразуем видео в наш внутренний формат, сберегаем его во временное хранилище, позже чего download-сервер (стрелочка вниз) раздаёт данный контент пользователям.

Разглядим подробнее реализацию всякого этапа.

Загрузка

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

Как выглядит наша система балансировки загрузки видео? У нас 6 серверов, по 2 в всяком датацентре. Для балансировки нагрузки внутри датацентров мы используем LVS (Linux Virtual Server). Это разрешает за одним IP-адресом беречь уйма серверов и перебрасывать на них запросы, в зависимости от их загруженности.

Для обеспечения отказоустойчивости по датацентрам мы используем DNS-GSLB. Пользователь нажимает кнопку «Добавить видео», получает некоторый линк на загрузку, дальше доменное имя авторизовывается в IP-адрес одного из датацентров. DNS-GSLB — это Global Server Load Balancing, его основная функция заключается в обеспечении отказоустойчивости на ярусе датацентров. В случае отказа датацентра наш DNS не будет разрешать (resolve, резолвить) доменное имя в данный датацентр.

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

Сейчас о том, как мы реализовали stateless-сервис. Пользователь начинает загрузку на определенный сервер, а тот, в свою очередь, блоками порядка мегабайта отправляет данные в распределенное хранилище (о нем побеседуем позднее), непременно по нескольким датацентрам. Upload-сервер реализован на Java, применяется Apache Tomcat.

Что происходит, когда мы теряем сервер, датацентр, либо, скажем, пользователь легко восстановил коннект через некоторое время позже обрыва соединения? Наш новейший upload-сервер, у которого нет никакой сессии с этим пользователем, по состоянию распределенного хранилища определяет последние удачно загруженные байты, и возобновляет загрузку с места разъединения. Отказоустойчивость фактически стопроцентная. Даже если в процессе загрузки утрачен датацентр, то процесс возобновляется с того места, где произошёл обрыв.

Трансформация (перекодировка)

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

При выборе формата мы проанализировали следующие данные: нынешнюю распространенность браузеров и операционных систем, в том числе Android и iOS, вероятность проигрывания в них видео через HTML5 и Flash. Так как нынешний плеер Одноклассников реализован на Flash, мы приняли решение поддерживать как HTML5-видео, так и Flash.

Разглядев форматы, которые поддерживаются Flash и HTML5, мы предпочли МР4, в качестве медиаконтейнера — кодек Н264, аудиокодек — ААС.

Выходит, позже того, как видео удачно загружено, оно должно попасть на реформирование в наш внутренний формат. Это осуществляется на серверах трансформации, где мы используем нативную библиотеку FFMpeg. Серверов трансформации у нас теперь порядка 60 штук. Что происходит с видео? Upload-сервер загружает видео во временное хранилище и в конце загрузки добавляет в очередь задачу на трансформацию.

Задания раздаются на серверы, но дело в том, что FFMpeg довольно нестабильная штука, она изредка зависает, съедает все источники системы, и бывают такие случаи, когда видео не конвертируется с первого раза. Следственно мы добавили непрерывное отслеживание ранга исполняемых задач. То есть процесс выглядит так: upload-сервер загрузил видео в очередь, сервер трансформации взял задачу и с некоторым промежутком проверяет ранг её выполнения. В случае отказа сервера, задание перезапускается, либо возвращается обратно в очередь и передается на иной сервер.

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

Эта схема восхитительно работает, но у нас возникает Single Point of Failure, это наша очередь. В случае, если мы теряем датацентр, где у нас хранится очередь, либо сам сервер, то у нас будут важные задачи. Здесь бы нужно реализовать какое-то решение распределенной очереди, но мы не эксплуатировали такие вещи и не хотели усложнять инфраструктуру. Мы легко применяли Zookeeper, которым Одноклассники дюже энергично пользуются в других своих сервисах.

Что такое Zookeeper? Это сервер распределенных блокировок, на нем есть готовые решения в виде выбора главы. Выбор главы — это такой процесс в распределенной системе, когда в случае потери сервера каждая система придет к стабильному состоянию вследствие назначению «главы». Он знает о том, что он «глава», и все серверы тоже это знают.

На самом деле, в этой системе «кандидат1» и «кандидат2» ничего не делают, это легко запасные серверы, расположенные в других датацентрах. Очередь всецело исполняется на «лидере». В случае потери «главы» выбирается новейший, и каждая система узнаёт о том, что 1-й из кандидатов стал новым «лидером».

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

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

В процессе создания различных по качеству версий мы столкнулись с такой задачей. Оказалось, что 15% загружаемого видео в заголовке имеют неверную длину. То есть, скажем, в заголовке ролика указана длительность 2 часа, а на самом деле видеопотока там на пару минут. Столкнувшись с этим, нам пришлось рассчитывать длину по кадрам, данный параметр мы используем как в расчете выходных параметров видео, так и для нарезки превьюшек.

Хранение

Позже нарезки видео оно отправляется в хранилище. Теперь оно состоит приблизительно из 70 серверов, на всяком из которых 36 двухтерабайтных дисков. Суммарно 5 петабайт. Хранилище распределенное, отказоустойчивое, оно именуется One Blob Storage (OBS). Для хранения состояния кластеров мы используем Zookeeper. Видео хранятся на дисках блоками по 64 мегабайта. Для этого хранилища выход жесткого диска из строя — не задача. На серверах имеются добавочные диски. В случае выхода из строя одного из них, на ином механически восстанавливается его состояние. Всякое видео мы бережем в 3 экземплярах. Таким образом, из всякого видео получается четыре новых, всякое из которых сохраняется в 3 экземплярах, по одному в всяком ДЦ.

Раздача контента

Вероятно, самая увлекательная часть. Требования:

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

В тезисе, для реализации псевдо-стриминга, то есть отдачи видео с всякого момента, у nginx есть подходящие модули. Но nginx работает с файлами, а мы хотели кэшировать только то, что пользователь подлинно глядит. Скажем, у нас двухчасовый фильм, а пользователь посмотрел порядка 5 минут. Тогда мы кешируем только данный ломтик. Пришлось вновь писать свое решение на Java.

Посмотрим, для начала, что такое псевдо-стримминг. При воспроизведении видео, в нем есть какая-то буферизированная часть. Если пользователь переходит в небуферизированную часть, то мы обязаны без заблаговременной буферизации начать воспроизведение с этого места. Как это устроено? Во время трансформации видео в наш внутренний формат, мы чередуем видео и аудио-сэмплы, а также всякие несколько секунд вставляем так называемый синк-сэмпл (sync sample). Получающийся в результате итог выглядит дальнейшим образом: у нас есть заголовок и есть блоки. Соответственно, когда пользователь выбирает какое-то место, то мы понимаем, с какого блока дозволено начать отдавать данные. В заголовке указаны начала всякого блока.

С одной стороны, выигрышно применять много таких блоков. Но тогда растет объем заголовка, и это тот объем, тот, что пользователь вынужден скачать до возникновения первого кадра видео. Пытаясь оптимизировать заголовок, мы пришли к такому решению: наивысший объем заголовка видеофайла не должен превышать 6 Мегабайт. При этом в одном видео получается 500-700 блоков.

В процессе нарезки, помимо выбора частоты синхронизации видео и аудио, возникла еще одна задача. Типовые преобразователи видео традиционно помещают заголовок в конец файла. Это им значительно комфортнее, потому что все смещения всех блоков теснее вестимы. И в случае с потоковым видео основное — не позабыть передвинуть заголовок в предисловие.

Псевдо-стримминг

Разглядим реализацию псевдо-стримминга в HTML5. Плеер имеет moov atom, при выборе какого-то места рассчитывает upset, сам запрашивает byte-range-request-данные и обеспечивает нативный поиск. У Flash-плеера обстановка несколько дрянней, он легко делает при старте запрос на сервер (в URL есть время старта в секундах), а дальше сервер должен ему подготовить полновесный видеофайл начиная с этого места, то есть пересчитать заголовок, как словно бы видео запущено ровно с момента запроса пользователя.

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

Разглядим архитектуру нашего раздатчика. Это такая кэширующая прослойка между нашим непрерывным хранилищем, она имеет несколько ярусов кэшей. Исторически так сложилась, что нам на серверах выдается порядка 96 Гигабайт RAM. Сюда мы ставим еще SSD-диски.

Алгорифм работы такой: пользователь запросил видео с сервера; если оно закэшировано в памяти, то мы отдаем его, если нет, то проверяем диск; если и на нем нет, то идем в наше хранилище. Видео из хранилища мы сразу помещаем в оперативную память и раздаем пользователям. В оперативной памяти мы используем только LRU-кэш и при вытеснении проверяем счетчик, сколько у нас просмотров этого сегмента. Если секция глядели огромнее определенного числа раз, то мы вытесняем его на диск; если секция просматривался немного, то мы его выбрасываем.

Требования к кэшу объемом в 96 гигабайт:

  • Кэш не должен оказывать могущества на GC, он должен быть LRU. Теперь у нас на сервере обрабатывается порядка 20 тысяч запросов в секунду, то есть он должен быть высокопроизводительным.
  • Кэш должен быть персистентным, то есть сохраняться позже перезапуска либо апдейта обслуживания раздачи.

Требования ко второму ярусу кэширования на дисках у нас предъявляются больше низкие. Довольно применять FIFO-кеширование, также используется внутреннее решение OBS, только локально.

Крупные объемы в 100 гигабайт и закроет на воздействие на GC приводят нас к offheap-кэшированию. Вариантов offheap-кэширования, в тезисе, много. Есть через Direct ByteBuffer, memory-mapped файлы, но у них есть задачи. Скажем, нативный код не переносим, а в этих решениях лимитация максимального объема выделяемых файлов порядка 2 гигабайт. Безусловно, дозволено их собрать из блоков, но это будет не дюже комфортно. Вариант решения: применять shared-memory, для обращения к ней применять Unsafe. Все это реализовано на нашей open source-библиотеке one-nio, ее дозволено посмотреть на github.

Для раздачи мы используем селекторы. Наш раздающий HTTP-сервер — это часть библиотеки one-nio. Сервер оборудован двумя гигабитными оптоволоконными картами. Для доступа к хранилищу у него имеется 4 гигабитных Ethernet-карты.

По итогам тестирования мы удачно справляемся с раздачей 20 Гигабит. То есть столько, сколько нам могут обеспечить внешние сетевые карты. При этом нагрузка на процессор порядка 30%. Теперь наши инженеры работают над тем, Дабы переконфигурировать систему и начать раздавать огромнее. Основное лимитация — 6 слотов на одном сервере и результативность нашего кэша. Она у нас получилась где-то 80 /20: 80% запросов блоков берется из кеша, 20% запросов достаются из OBS. Вероятно, это хорошо, но для 20 Гигабит мы примерно всецело выбираем источник 4 гигабит Ethernet-доступа к нашему непрерывному хранилищу.

Балансировка

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

Нам необходимо было сделать так, Дабы на 10 серверах всеобщий размер кэша составил около терабайта. Для этого написали свой балансировщик. Основная его идея в том, что все видео разбиваем на какое-то число партиций (основное, Дабы их было много), и за всяким сервером закрепляем какие-то партиции. Собственно, всякий сервер раздает только свой видео-контент. Для обеспечения отказоустойчивости (равномерного разделения нагрузки по иным серверам кластера) в всякую партицию добавляем реплики.

Если Сервер упал, соответственно, раздача происходит этой партицией с одного сервера, а иной партицией — с иного. Нагрузка равномерно распределяется по каждому серверам, и все бы здорово, но у этих серверов контент не закэширован. Таким образом, мы образуем огромное число запросов в наше непрерывное хранилище. Оно может такого и не вынести, исключительно, в часы пик. Для этого мы «прогреваем» партиции на грядущее, то есть изредка отправляем пользователя с некоторой вероятностью на реплики — на реплику1 либо реплику2. Таким образом, мы убиваем сразу 2-х зайцев: прогреваем данный кэш и балансируем нагрузку на хранилище, а с иной стороны, результативно используем кэш внутри кластера.

Также не забывайте, что на этом HTTP-сервере byte range реализуется легко, а вот реализовать перестроение moov atomс какого-то места не самая простая задача. Само перестроение происходит на раздающих серверах. Для этого мы испробовали воспользоваться MP4-парсером, но он генерировал дюже много мусора, оказывал огромное воздействие на GC и был весьма неэффективен. У нас число запросов на перемотку — сотня в секунду. Мы отказались от МР4-парсера и реализовали свое решение на Java для перестроения moov атома с какого-то времени. Оно оказалось весьма результативным, заголовок перестраивается из 6 мегабайт в 3 за время до десяти миллисекунд.

Есть такой увлекательный факт: в августе 2012 года, когда марсоход совершил посадку на Марс, НАСА решило организовать онлайн-трансляцию видео. Для этого они пригласили nginx. У nginx была платформа, раздающая, кэширующая, развернутая на 40 Amazon-ЕС2-серверах. Это, безусловно, не суперэффективные серверы, но 40 штук — довольно серьезно. Была приглашена тестирующая компания, которая проверяла нагрузку на данный видеосервис, и тестировали они нагрузку порядка 25 гигабит в секунду. Я думаю, что один наш сервер позже некот

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB