Как устроена контент-система Турбо-страниц: схемы, факты и немного истории

hqa5mvtylx0m3ns_yixifxt1qw8.png

По данным TelecomDaily, почти 30% пользователей мобильного интернета в России ежедневно сталкиваются с проблемами при загрузке сайтов. Однако причина может быть не только в неравномерном покрытии, но и в слишком большом «весе» страницы.

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

Как работает эта магия? Какой путь проходят данные, прежде чем стать полноценной Турбо-страницей? Меня зовут Стас Макеев, я руковожу разработкой технологии Турбо-страниц. Сейчас попробую всё объяснить.

Но сначала — что-то вроде краткого содержания, чтобы вы не потерялись, когда начну углубляться в детали.

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

У владельцев сайтов есть несколько способов подключиться к системе:

  • зарегистрировать в Яндекс.Вебмастере фиды: YML — для интернет-магазинов, RSS — для контентных сайтов;
  • передавать материалы через API: хозяин ресурса сам загружает на сервера Яндекса необходимые данные (пока только для контентных сайтов);
  • включить автопарсер: Турбо-страницы строятся на основании исходных материалов контентного сайта.


Контент-система складывает результаты своей работы в специальное хранилище типа «ключ-значение» (key-value-хранилище или KV-хранилище), где ключом является URL оригинального сайта, а в значении хранится контент Турбо-страницы. Как только данные попадают в это KV-хранилище, очередная Турбо-страница сразу же становится доступна пользователям поиска, а в сервисах Яндекса у соответствующего документа появляется специальный значок с ракетой. Также для ускорения работы мы кешируем картинки и видео в наших CDN.

Очень упрощенно общая схема работы выглядит так:

aomqu5polx3u8fse71spyit1eam.png

С чего все начиналось


Самая первая версия контент-системы была устроена совсем просто: каждые несколько минут, по расписанию, на сервере внутреннего облака Яндекса запускалась одна и та же программа. Она состояла из нескольких шагов, каждый следующий запускался после того, как были готовы данные предыдущего для всех известных нам фидов:

  • скачивался список RSS-фидов, запускался парсер документов;
  • из результатов парсера извлекался список изображений;
  • в CDN загружались ещё не закешированные картинки;
  • обработанные документы заливались в KV-хранилище.


Такая схема отлично работала, когда система имело дело с несколькими тысячами довольно лёгких RSS-фидов новостных агентств (суммарно — информация о чуть менее 100 000 документов). Но с увеличением количества фидов быстро обнаружилась проблема: каждый шаг занимал всё больше времени, росла задержка между появлением нового документа в исходном источнике и его отображением в Турбо-режиме.

Ситуацию удавалось держать под контролем с помощью различных ухищрений: первым делом мы выделили первый шаг (скачивание RSS-фидов + парсер документов) в отдельный процесс. То есть, пока один обрабатывал картинки для предыдущей итерации, другой процесс уже скачивал фиды для следующей. Через некоторое время стало понятно: в таком виде систему очень сложно масштабировать. Нам нужно что-то принципиально новое.

Обработка RSS, API и YML в новой контент-системе


Главная проблема старой контент-системы — все данные обрабатывались одним куском: перехода к следующему шагу не происходило, пока каждый документ не прошёл предыдущий. Чтобы избавиться от этого, было решено построить некий конвейер: пусть фиды и отдельные документы обрабатываются максимально независимо друг от друга. Все шаги выделили в отдельные кубики-сервисы — на верхнем уровне схема получилась такая:

g6lpxdauqtwxm9alh-mrgyf_wro.png

  • первый кубик качает RSS-фиды и передает дальше;
  • второй — забирает фиды по одному, парсит содержимое. На выходе — отдельные документы;
  • третий — забирает документы по одному, обрабатывает картинки и видео, записывает всё в KV-хранилище.


Одни и те же фиды могут быть зарегистрированы не только в Турбо, но и на других наших сервисах — в Новостях или в Маркете, например. Если каждый из них будет скачивать данные самостоятельно, нагрузка на сервера вебмастеров в несколько раз превысит допустимую. Как правильно? Скачать фид один раз, а затем содержимое раздать всем сервисам-потребителям — этим занимается Яндекс.Робот. Его же услугами мы пользуемся для выгрузки контента: забираем из Яндекс.Вебмастера список зарегистрированных в Турбо RSS- и YML-фидов, передаем его в Робота и подписываемся на результаты скачивания.

На полученных данных запускаем парсер. На всякий случай напомню: RSS-фид — просто файл в формате ».XML», доступный по статическому URL на хосте партнера. В этом файле расположена информация обо всех обновлениях на сайте — какие документы новые, какие измененные. В идеальных фидах находилась бы только самая актуальная информация за последние несколько часов: не более 100 документов на несколько сотен килобайт.

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

Обработка YML-фидов и API с точки зрения контент-системы практически ничем не отличается от взаимодействия с RSS: для YML запускаем другой парсер, а данные, переданные через API, получаем напрямую из Яндекс.Вебмастера.

rehhgz4wtfo8nqylaexh74z5dga.png

Обработка изображений и видео


Документ, который получается на выходе из парсера, практически готов для записи в KV-хранилище. Единственное, что осталось сделать перед отправкой — обработать изображения и видео: закешировать в CDN и заменить ссылки в документе. Тут снова обращаемся за помощью к Роботу.

Первым делом проверяем каждую картинку и видео: есть ли они в CDN? Если все уже закешировано, заменяем ссылки и обновленный документ отправляем в KV-хранилище. В противном случае:

  • список недостающих URL отправляем в Робот, для планирования и скачивания;
  • сам документ — во временное хранилище, чтобы через некоторое время попробовать проверить его ещё раз.


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

2ng4oz6c5ocn3jjbld4armtcewk.png

Типичный путь, который проходит новый документ:

  • документ появляется в фиде;
  • Робот скачивает фид;
  • парсер обнаруживает новый документ и отправляет его дальше;
  • проверяем, что картинки из документа не упоминаются в базе данных, заказываем скачивание, документ отправляем во временное хранилище (Delay). Пока документ находится там, Робот скачивает картинки, они кешируются в CDN, а ссылки появляются в базе данных;
  • во второй раз проверяем картинки в базе данных, находим ссылки на CDN, обновляем документ и отправляем в KV-хранилище.
  • Обратите внимание: если картинки скачиваются долго, документ может несколько раз побывать в Delay.


Автопарсер


Есть ещё один способ подключения к Турбо-страницам, для которого вебмастеру практически ничего не нужно делать — Автопарсер. Он строит Турбо-страницы на основании исходных данных контентного сайта. Подключиться, посмотреть примеры готовых страниц, настроить рекламу и аналитику можно в Яндекс.Вебмастере.

Основная сложность, с которой сталкивается Автопарсер — распознать по HTML-разметке, какая информация основная и должна быть использована при построении Турбо-страницы. Для этого у нас есть несколько оффлайн-процессов, которые пытаются понять, как именно нужно парсить конкретный хост. Остановлюсь на двух основных:

  • Первый скрипт анализирует документы из RSS-фидов и соответствующие им HTML-страницы оригинального сайта. Итог — разбиение страниц на фрагменты, содержимое которых либо целиком вошло в RSS-аналог (то есть является основной информацией), либо полностью отсутствует. Так формируется огромная, достаточно качественная обучающая выборка. На ней при помощи технологии машинного обучения CatBoost тренируются модели, предсказывающие какие тексты, фото и видео исходной страницы ключевые. На больших материалах работает отлично, но для маленьких модель может легко перепутать основной текст и, например, меню. К счастью, почти всегда статьи в рамках одного сайта устроены очень похоже. То есть, применяя модель для множества страниц, можно заметить, что элементы находящиеся на определенных путях в HTML разметке часто попадают в результат. Совпадение? Едва ли: такое содержимое всегда оказывается основным. Итог — некоторый набор обобщённых правил, как корректно парсить хост.
  • Другой процесс контролирует качество: результат применения этих правил к страницам выборочно валидируется специальным заданием в Яндекс.Толоке. Список хостов, для которых получилось хорошо, передаётся в Робота, чтобы по привычной уже схеме подписаться на результаты скачивания документов, окончательные правила — в парсер. Документ после обработки парсером отправляется дальше — для кеширования картинок.


Кстати, ещё одно частое препятствие — многие сайты закрывают в robots.txt возможность скачивания картинок роботами. Обойти эту проблему, к сожалению, невозможно, и для таких страниц Автопарсер недоступен.

В итоге полная схема контент-системы выглядит вот так:

lljx-adtt8ykngcnfnutykobxr4.png

Система получилась хорошо масштабируемой: уже сейчас для обслуживания базы данных, автопарсера и других компонентов системы используется значительное количество ресурсов (только в кубике, отвечающем за парсинг RSS, YML и API, задействовано более 300 процессорных ядер), а в случае роста нагрузки подключить дополнительные мощности будет не слишком сложно.

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

© Habrahabr.ru