Pull to refresh
141.54
ОК
Делаем продукт, который объединяет миллионы людей

Загрузка видео «без единого разрыва»

Reading time 9 min
Views 33K
Видео – один из самых популярных сервисов на Одноклассниках. Чего только не грузят наши пользователи: от милых сюжетов с детского утренника до снятых на видеорегистратор аварий. Поэтому быстро и стабильно работающая загрузка видео важна нам не только как одна из самых востребованных пользователями функций, но и как необходимое условие для генерации контента.

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


Да, это



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


Цифры решают всё

Ежедневно пользователи Одноклассников загружают почти двести тысяч видеороликов, а с аудиосообщениями (да-да, мы хостим аудиосообщения на том же кластере, что и видео) количество ежедневных загрузок превышает миллион.

Каждый день к нам на серверы попадает 15-20 терабайт нового видео, пиковый входящий трафик до 5 гигабит/сек а исходящий достигает 500 гигабит/сек. Большинство загружаемых видео — это короткие ролики, снятые пользователями на смартфоны. В то же время по суммарному объему загрузок в терабайтах web все еще остается лидером.

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



Разумеется, вместе с повышением качества растет и размер загружаемых видеофайлов: при переходе в качестве на 1 шаг вверх (720p —> 1080p, 1080p —> 1440p и т.д.) объем дискового пространства, занимаемого видео, возрастает примерно в два с половиной раза.

Давайте посмотрим, как реализована загрузка видео на портал в наших клиентах: в браузере (веб-версия), в мобильных приложениях (iOS/Android/WinPhone) и на мобильном портале.

Web-версия

Десятки миллионов пользователей. Кроме высоких нагрузок эти три слова означают, что мы должны поддерживать весь имеющийся на сегодняшний день зоопарк браузеров начиная с IE 8 и заканчивая еще не вышедшими ночными сборками Firefox.

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

А если автоматически обновляются — тоже могут быть проблемы. Вот, вышел неделю назад Firefox 40, и у миллионов наших пользователей, которые автоматически проапдейтились на него, сломалась загрузка фоток. Мы быстро все починили (в этот раз во всем были виноваты несовместимости в Content Security Policy), но бывает, что починка занимает довольно существенное время.


Как же нам удалось добиться стабильной работы портала в таком зоопарке браузеров? Fallback, fallback и еще раз fallback!

Клиентская часть нашего загрузчика видео написана на JavaScript c использованием фреймворков RequireJS, jQuery и замечательной библиотеки FileAPI от RubaXa из Mail.Ru.

FileAPI определяет, поддерживает ли пользовательский браузер HTML5. Если поддерживает, то все процедуры выполняются с помощью HTML5. Если не поддерживает — то FileAPI сам переключается на Flash. Также от браузера пользователя зависит и размер отправляемых chunk’ов.

Для каждого браузера мы экспериментальным путём подобрали свой размер чанка. В среднем он равен 2 Мб, а диапазон колеблется от 100 Кб до 10 Мб.


Загрузка файлов сделана максимально удобной для юзера. Поддерживается джентльменский набор функций: drag&drop, одновременная загрузка нескольких файлов, автоматически возобновляемая загрузка, кнопка паузы, индикатор хода выполнения и т.д. и т.п.

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



До кучи FileAPI не накладывает никаких ограничений на размер загружаемых пользователем файлов. При желании можно загрузить даже терабайтный файл — какое-нибудь многочасовое видео в разрешении 4К. Другое дело, что ждать окончания загрузки пользователю придётся довольно долго даже при широком канале.

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



Логика этого процесса очень проста. FileAPI отправляет на upload-сервер один чанк и ждёт ответа. Если ответ не приходит в течение некоторого времени, клиент отправляет этот чанк снова, потом еще раз и еще раз, до тех пор, пока сервер не подтвердит успешное принятие. В свое время переход на FileAPI позволил нам в несколько раз сократить число ошибок во время загрузки.



Мобильные устройства

Если говорить о количестве загрузок видео на портал, то картина получается такая:



Мобильные пользователи могут загружать видео двумя способами — из браузера и из приложения. В мобильных браузерах загрузка возобновляется при обрыве сети, а в мобильных приложениях — еще и после перезагрузки телефона (например, в случае разрядки батареи).

Клиентская часть мобильного загрузчика сначала пытается залить сразу весь файл от начала до конца, используя XMLHttpRequest.send(). В случае обрыва соединения загрузчик пытается восстановить связь с сервером, опрашивая статус загрузки. Если удаётся получить ответ от сервера, загрузчик использует Blob.slice() для того, чтобы получить блок данных, следующий за последним успешно загруженным байтом.

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

Если браузер поддерживает HTML5 Web Worker API, то загрузка осуществляется в отдельном потоке, что помогает сделать нам интерфейс более отзывчивым.

Чтобы показать пользователю прогресс загрузки, мы используем событие XMLHttpRequest.onprogress. В браузерах это событие реализовано как попало: некоторые отправляют тысячи событий каждую секунду, другие не отправляют их вообще. Для первых реализован троттлинг — большая часть событий игнорируется. Для вторых — происходит периодический опрос сервера (раз в 5 секунд).

Бэкенд загрузки видео. Архитектура

В связи с постоянным ростом нагрузки мы непрерывно расширяем и модернизируем инфраструктуру нашего видеосервиса. Сегодня сервис видео — это
  • 240 серверов для хранения пользовательских видео. Дисков там более 7000 — суммарно около 30 петабайт;
  • метаданные и кэш развёрнуты еще на 36 машинах;
  • за трансформацию видеороликов в наш внутренний формат отвечают еще 150 серверов;
  • наконец, еще 36 машин используются для раздачи и загрузки видео.

Таким образом наш видеосервис состоит из более чем 400 машин. Железо у нас в целом довольно обычное — что-то типа двухпроцессорных E5-2620 с 64Гб RAM, но сервера трансформации имеют более мощные CPU, а на серверах раздачи — 256Гб RAM и 10Тб SSD. Архитектура хорошо скалируется, позволяет нам запросто использовать эти дешевые и, в общем-то, уже далеко не новые процессоры и тем самым существенно экономить на железе. Экономика тут простая: каждая $1000 экономии в цене сервера выливается нам в полмиллиона долларов экономии на всем видеосервисе. А если говорить про портал в целом, где серверов у нас сейчас около 7 тысяч, то картина становится еще приятнее :)

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


За загрузку видео в Одноклассниках отвечает подсистема из 6 серверов, распределённых по трем датацентрам. Перед каждой парой upload-серверов в ДЦ стоит кластер LVS серверов (Linux Virtual Server — это модуль ядра Linux, который позволяет распределять IP трафик на любое количество физических серверов).

Для балансировки запросов между ДЦ используется DNS-GSLB (Global Server Load Balancing). Это сервера, которые резолвят доменное имя в IP наиболее разгруженного/доступного датацентра. В случае отказа одного из датацентров DNS-GSLB равномерно перераспределит нагрузку по оставшимся.



Рассмотрим процесс загрузки видео:
  • пользователь инициирует загрузку, и сервер выдает ему URL для загрузки: http(s)://vu.mycdn.me/upload.do?...
  • далее DNS-GSLB разрешает доменное имя vu.mycdn.me в IP одного из LVSов (на рисунке в ip1);
  • DNS-GSLB партиционирует пользователей по первым трем октетам их IP адреса. Таким образом, одному пользователю при повторных DNS резолвах выдается IP одного и того же датацентра. Выдача IP-адреса другого ДЦ произойдет только при его отказе или смене IP пользователя, что случается редко;
  • далее LVS внутри ДЦ уже перенаправляет запросы к менее загруженному и доступному серверу;
  • на LVS настроен IP affinity, который обеспечивает отправку всех пользовательских запросов к одному серверу.


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

Параллельно процессу загрузки видео на upload-сервер полученные данные отправляются в распределённое хранилище. Для каждого пользователя на сервере открыта сессия и буфер для приема данных. Размер буфера — 10 МБ. Как только старый буфер заполняется, одновременно происходят две вещи:
  • открывается новый буфер;
  • запускается асинхронная операция сброса старого буфера в промежуточное распределенное хранилище.

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

В случае выхода из ротации одного из серверов внутри ДЦ LVS передаст все запросы доступному upload-серверу внутри ДЦ. Доступный upload server восстанавливает состояния клиентской сессии по данным, имеющимся в распределенном хранилище, и пользователь незамедлительно продолжает загрузку, в худшем случае с retry последних загруженных 10 Мб.



Когда пользователь переключается на другой сервер, может появиться «дырка» в последовательности загружаемых байт. В этом случае сервер возвращает специальный код ошибки 416 — «Range is not acceptable error, recoverable» с заголовком «X-Last-Known-Byte». Если клиент поддерживает данный заголовок, то он возобновляет загрузку с места, указанного в этом заголовке, а если нет — идет на один чанк назад.

В случае выхода ДЦ из строя (ситуация более редкая, чем выход из строя сервера) клиентская библиотека загрузки файлов (например, FileAPI) будет в течение «ретрай-времени» пытаться возобновить загрузку по IP LVSа, расположенного в упавшем ДЦ. Все новые загрузки продолжатся через доступные ДЦ.



Отказоустойчивость бэкэнда

Три основных сценария для проверки отказоустойчивости нашего сервиса — это:
  • сценарий с выходом датацентра из строя;
  • сценарий с перегрузкой по диску;
  • сценарий с перегрузкой по трафику.


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

Сценарий с перегрузкой по входящему трафику для нас тоже малореален. Каналы, которые мы приобретаем в датацентрах, симметричны, и поскольку наш download трафик превосходит наш upload трафик примерно в 100 раз, то перегрузку по upload мы тоже всерьез не считаем за угрозу. Основная наша защита здесь — рубильники, которые отключают части пользователей загрузку видео. Таких частей (партиций) у нас 256, поэтому мы можем регулировать количество пользователей, которые могут загружать (или наоборот просматривать) видео, с шагом меньше, чем 0,4%.

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

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

Факап со шпинделями

В этом году нам представился случай оценить степень отказоустойчивости нашего сервиса.

После Парада Победы 9 мая начался резкий рост загрузок видеозаписей парада и салюта. Мы не ожидали трехкратного увеличения пикового трафика, поэтому дисковая подсистема временного хранилища очень быстро оказалась загруженной на 100%. Upload-сервера стали получать ошибки при попытке сбросить чанк во временное хранилище — хранилище не отвечало. У каждой сессии есть буфер, в который принимаются входящие данные. Буферы жрут место, поэтому место в оперативке для сессий на upload-серверах тоже быстро закончилось…

Т.е.
  • клиентский загрузчик хотел загрузить чанк
  • в буфере сессии не было места
  • сервер возвращал клиенту возобновляемую ошибку.

Клиентский загрузчик повторял попытки до тех пор, пока у upload-сервера не появлялся свободный буфер (пока upload-серверу не удавалось сбросить буфер в распределенное хранилище).

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

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

Выводы

  • Если у вас в требованиях работа во многих браузерах, то используйте готовые решения для fallback — FileAPI для загрузки файлов, Atmosphere и Pusher для push-нотификаций и т.п.;
  • для лучшего баланса между производительностью и кроссбраузерностью имеет смысл менять размер чанка в зависимости от клиентского браузера. Дефолтный размер чанка — 2 Мб, но есть проблемные браузеры, для которых этот размер нужно уменьшать до 500 Кб, а то и до 100 Кб;
  • практически любую высоконагруженную систему имеет смысл тестировать на отказоустойчивость несколькими различными сценариями, в том числе имитируя аварии.

Ну и не забывайте про мобильные устройства — мобильный трафик на крупных порталах давно уже превысил трафик с десктопов.
Tags:
Hubs:
+26
Comments 12
Comments Comments 12

Articles

Information

Website
oktech.ru
Registered
Founded
Employees
201–500 employees
Location
Россия
Representative
Юля Новопашина