Загрузка видео «без единого разрыва»
Видео – один из самых популярных сервисов на Одноклассниках. Чего только не грузят наши пользователи: от милых сюжетов с детского утренника до снятых на видеорегистратор аварий. Поэтому быстро и стабильно работающая загрузка видео важна нам не только как одна из самых востребованных пользователями функций, но и как необходимое условие для генерации контента.
- В процессе загрузки у пользователя может пропадать соединение с нашим порталом (закрыл ноут, вошел в планшетом в лифт, сел аккумулятор на телефоне и т.п.)
- Старые устройства не поддерживают современные технологии загрузки (а у нас миллионы пользователей имеют слабые смартфоны или древние браузеры)
- При том количестве пользователей, которые есть у нас, задача о стабильной заливке видео превращается в задачу о стабильной загрузке видео в огромных объемах.
В этой статье мы расскажем о том, как мы победили все эти проблемы, опишем архитектуру нашего решения и причины, по которым она получилась именно такой.
Цифры решают всёЕжедневно пользователи Одноклассников загружают почти двести тысяч видеороликов, а с аудиосообщениями (да-да, мы хостим аудиосообщения на том же кластере, что и видео) количество ежедневных загрузок превышает миллион.
Каждый день к нам на серверы попадает 15-20 терабайт нового видео, пиковый входящий трафик до 5 гигабит/сек а исходящий достигает 500 гигабит/сек. Большинство загружаемых видео — это короткие ролики, снятые пользователями на смартфоны. В то же время по суммарному объему загрузок в терабайтах web все еще остается лидером.
Изменилось и качество видео — камера формата Full HD давно стала стандартом комплектации любого мобильного устройства. По мере выхода из обращения старых смартфонов и приобретения новых доля загружаемых пользователями видеороликов в высоком и сверхвысоком разрешении неумолимо растет. За последний год число загружаемых роликов в качестве выше чем Full HD выросло более чем вдвое.
Разумеется, вместе с повышением качества растет и размер загружаемых видеофайлов: при переходе в качестве на 1 шаг вверх (720p —> 1080p, 1080p —> 1440p и т.д.) объем дискового пространства, занимаемого видео, возрастает примерно в два с половиной раза.
Давайте посмотрим, как реализована загрузка видео на портал в наших клиентах: в браузере (веб-версия), в мобильных приложениях (iOS/Android/WinPhone) и на мобильном портале.
Web-версияДесятки миллионов пользователей. Кроме высоких нагрузок эти три слова означают, что мы должны поддерживать весь имеющийся на сегодняшний день зоопарк браузеров начиная с IE 8 и заканчивая еще не вышедшими ночными сборками Firefox.
Новые версии браузеров выходят чаще, чем раз в день. И понятно, что далеко не все наши пользователи спешат обновляться.
Как же нам удалось добиться стабильной работы портала в таком зоопарке браузеров? Fallback, fallback и еще раз fallback!
Клиентская часть нашего загрузчика видео написана на JavaScript c использованием фреймворков RequireJS, jQuery и замечательной библиотеки FileAPI от RubaXa из Mail.Ru.
FileAPI определяет, поддерживает ли пользовательский браузер HTML5. Если поддерживает, то все процедуры выполняются с помощью HTML5. Если не поддерживает — то FileAPI сам переключается на Flash. Также от браузера пользователя зависит и размер отправляемых chunk’ов.
Загрузка файлов сделана максимально удобной для юзера. Поддерживается джентльменский набор функций: 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 машин используются для раздачи и загрузки видео.
- возобновление загрузки в течение нескольких суток после потери соединения;
- работоспособность сервиса при программных ошибках и аппаратных отказах;
- гарантия сохранности загруженных данных при отказе сервера или потере целого датацентра;
- высокая производительность.
За загрузку видео в Одноклассниках отвечает подсистема из 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-сервер.
- открывается новый буфер;
- запускается асинхронная операция сброса старого буфера в промежуточное распределенное хранилище.
В случае выхода из ротации одного из серверов внутри ДЦ 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-серверах. Иногда клиентские загрузчики получали ошибку и возобновляли загрузку через некоторый интервал.
Итоги были таковы: в целом скорость загрузки на портал просела примерно вдвое, но отказа сервиса не произошло, и все пользователи в итоге догрузили свое видео.