Мои контакты


воскресенье, 5 июля 2015 г.

Проектирование высоконагруженных приложений. Часть 4. Отложенная обработка данных.

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

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


Масштабирование во времени.


Отложенная обработка.


Что происходит, когда вы добавляете кого-то в друзья на Facebook? Да куча всего. Обновляются какие-то счетчики, уходят уведомления, отправляются электронные письма и т.д. И это только то, что можно заметить будучи простым пользователем. Здесь основная мысль в том, что необязательно все это делать сразу по запросу. Например, электронное письмо можно отправить через полчаса, не страшно, правда ведь? Эту особенность и нужно брать на вооружение. 

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

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

Очередь.


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

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

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

Самой популярной реализацией очереди на данный момент является RabbitMQ, написанный на Erlang. Практически для всех языков есть готовые библиотеки для работы с этим инструментом. Мы используем именно RabbitMQ в работе.

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


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

Очереди для обеспечения надежности.


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

С помощью очереди вы можете позволить какой-нибудь части системы сломаться. Каждому сервису можно сделать свою внутреннюю очередь, а также же демона, который будет в нее класть задачи и общаться с внешним миром. Получается, что если сервис по какой-то причине "упал", все задачи все-равно сохранились, а после того, как сервис поднимется, эти задачи будут обработаны. Эта схема практически неубиваема и восстанавливается после сбоя без потери данных. Такой подход, на сколько мне известно, повсеместно применяется в банковских системах.


Мы рассмотрели очень мощный паттерн проектирования интернет-приложений — очередь. Нужно запомнить два основных момента: 1) с помощью очередей мы можем отложить выполнение операции "на потом" и обеспечить возможность масштабирования; 2) очереди могут выступать как средство межсервисной коммуникации и повышать надежность распределенных систем.

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

Спасибо людям, которые организуют Highload++ и делают движуху вокруг темы высоконагруженных проектов.