Масштабирование от нуля до миллиона пользователей

Настройка одного сервера

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

Рисунок 1-1 демонстрирует одиночный сервер, в котором всё запущено на 1 сервере: веб приложение, база данных, кеш и т.д.

Рисунок 1-1

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

Рисунок 1-2

  1. Пользователи получают доступ к вебсайтам через доменные имена, такие как api.mysite.com. Обычно Системы доменных имён (DNS) - это платный сервис, предоставляемый сторонними организациями и не обслуживаемый нашими серверами.
  2. Адрес интернет протокола (IP) возвращается в браузер или мобильное приложение. В примере возвращается адрес 15.125.23.214.
  3. Как только IP адрес получен, запрос Hypertext Transfer Protocol (HTTP) отправляется напрямую к вашему веб-серверу.
  4. Веб-сервер возвращает HTML-страницу или JSON-ответ. В общем случае может вернуть в теле ответа также изображения, видео и другие типы содержимого.

Далее, давайте проверим источники траффика. Траффик к нашему вебсерверу приходит из двух источников: web и мобильное приложения.

  • Web приложение: использует комбинацию серверных языков (Java, Python, etc.) для обработки бизнес-логики, хранения и т.д. и клиентских языков (HTML и JavaScript) для представления.
  • Мобильное приложение: протокол HTTP - протокол коммуникаций между мобильным приложением и веб-сервером. JavaScript Object Notation (JSON) - общеиспользуемый формат для передачи данных из-за его простоты. Пример API ответа в формате JSON представлен ниже:

GET /users/12 – Retrieve user object for id = 12 JSON response example

База данных

С ростом базы данных один сервер может оказаться недостаточным, нам понадобится несколько серверов: один для обработки web/mobile запросов, другой для базы данных (Рисунок 1-3). Разделение web/mobile траффика (уровень web) и базы данных (уровень данных) позволяет масштабировать их независимо.

Рисунок 1-3

Какую базу данных использовать?

Вы можете выбирать между традиционной реляционной базой и нереляционной базой. Давайте поймём, в чём разница. Реляционная база данных также называется реляционной системой управления баз данных (RDBMS) или SQL базой данных. Самые известные представители - PostgreSQL, MySQL, Oracle database, и другие.

Реляционная база данных представляет и хранит данные в таблицах и строках. Вы можете выполнять операции объединения используя язык запросов SQL над разными таблицами базы данных.

Нереляционные базы данных также называют NoSQL базами данных.

Известные представители - CouchDB, Neo4j, Cassandra, HBase, Amazon DynamoDB, MongoDB, etc

Эти базы данных группируются в 4 категории:

  • хранилища ключ-значение;
  • графовые хранилища;
  • колоночные хранилища;
  • документные хранилища.

Операции объединения обычно не поддерживаются в нереляционных базах данных.

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

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

  • вашему приложению нужны очень низкие задержки;
  • ваши данные не структурированные или у вас нет реляционных данных;
  • вам достаточно сериализовывать и десериализовывать данные (JSON, XML, YAML и др.);
  • вам нужно сохранять большие объёмы данных.

Вертикальное и горизонтальное масштабирование

Вертикальное масштабирование, именуемое также “масштабирование вверх” означает процесс добавления мощности (ядра, память) на ваши сервера. Горизонтальное масштабирование, именуемое также “масштабирование в ширину” позволяет масштабироваться путём добавления большего количества серверов в пул ресурсов. Когда траффик небольшой, вертикальное масштабирование - отличный вариант и простота вертикального масштабирования - это его главное преимущество. К сожалению, оно имеет серьёзные ограничения: * вертикальное масштабирование имеет жёсткий лимит. Невозможно бесконечно добавлять CPU и память к одному серверу; * Вертикальное масштабирование не имеет аварийного переключения и резервирования. Если один сервер упал, вебсайт / приложение упадёт целиком.

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

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

Балансировщик нагрузки

Балансировщик нагрузки распределяет входящий трафик по веб серверам, которые определены в балансируемом множестве. Рисунок 1-4 показывает, как работает балансировщик нагрузки.

Рисунок 1-4

Как показано на рисунке 1-4, пользователи соединяются с публичным IP балансировщика нагрузки напрямую. В этой конфигурации веб сервера больше недоступны напрямую с клиентов. В целях безопасности для коммуникации между серверами используются приватные IP. Приватный IP - IP адрес, доступный только между серверами одной сети; таким образом, они недоступны через интернет. Балансировщик общается с веб сервером через приватные IP.

На рисунке 1-4, после того как мы добавили балансировщик и второй веб сервер, мы успешно решили вопрос аварийного переключения и улучшили доступность web зоны:

  • Если сервер 1 падает, весь траффик будет переадресован на сервере 2. Это снижает вероятность недоступности вебсайта. Мы также добавим второй сервер в пул для балансировки нагрузки
  • если траффик вебсайта быстро расчёт, 2 сервер может быть недостаточно, чтобы обработать весь траффик, балансировщик нагрузки может помочь решить эту проблему изящно. Вам достаточно добавить больше серверов в пул и балансировщик начнёт отправлять запросы к ним.

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

Репликация баз данных

Цитата из википедии: “репликация баз данных может быть использована в системах управления базами данных, обычно с отношением primary/replica между источником (primary) и копиями (replica).

Primary база данных обычно поддерживает только операции записи. Replica берёт копию данных с primary и обслуживает запросы на чтение. Все операции изменения данных, такие как insert, delete или update должны быть отправлены в primary базу. Большинству приложений требуется намного больше операций чтения, чем записи. Поэтому количество реплик в системе обычно больше, чем количество primary баз данных. Рисунок 1-5 показывает primary базу данных с несколькими репликами.

Рисунок 1-5

Преимущества репликации баз данных:

  • Улучшение производительности: при использовании модели master-slave все операции записи и обновления происходят в master-узлах, тогда как операции чтения распределены между репликами. Такая модель увеличивает производительность, поскольку позволяет обрабатывать больше запросов одновременно.

  • Надёжность: Если какой-либо из серверов баз данных выходит из строя из-за катастрофы (например, пожара или землетрясения), данные остаются доступными. Можно не волноваться о сохранности данных, поскольку они реплицированы в несколько мест.

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

В предыдущем разделе обсуждался вопрос, как балансировщик нагрузки помогает увеличить доступность системы. Зададимся похожим вопросом и здесь: Что, если одна из баз данных отключится? Архитектура, представленная на рисунке 1-5, может дать ответ:

  • Если имеется только одна база-реплика, и она отключилась, операции чтения будут временно перенаправляться в master-базу. Как только проблема будет найдена, новая реплика заменит неработающую. Если же имеется несколько баз-реплик, запросы чтения будут перенаправлены на работающую копию, пока новая реплика не заменит неработающую.

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

Рисунок 1-6 показывает дизайн системы после добавления в неё балансировщика нагрузки и репликации баз данных.

Рисунок 1-6

Давайте взглянем на дизайн

  • пользователь получает IP адрес балансировщика нагрузки из DNS;
  • пользователь соединяется с балансировщиком нагрузки через этот IP адрес;
  • Http запрос направляется на сервер 1 или сервер 2;
  • веб сервер читает данные из базы slave;
  • веб-сервер направляет операции изменения данных на базу master. Это включает в себя операции insert, update и delete;

Сейчас у вас есть уверенное понимание зон веб и данных. Настало время улучшить время загрузки / ответов. Это может быть сделано добавлением кеширующего слоя или доставкой статического контента (JavaScript/CSS/картинки/видел файлы) в сеть доставки контента (CDN).

Кеш

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

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

Зона кеширования

Зона кеширования - временный слой хранения данных, более быстрый, чем база данных. Преимущества зоны кеширования включают лучшую производительность системы, возможность снизить нагрузку на базу данных, возможность масштабировать кеш зону независимо. Рисунок 1-7 показывает возможную конфигурацию сервера кеширования:

Рисунок 1-7

После получения запроса веб сервер сначала проверяет, доступен ли в кеше ответ. Если да, отправляет ответ клиенту. Если нет, отправляет запрос в базу данных, сохраняет ответ в кеше и отправляет клиенту. Эта стратегия кеширования называется “сквозной кеш”. Другие стратегии кеширования зависят от типов данных, размера и моделей доступа.

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

SECONDS = 1
cache.set ('myKey', 'myValue', 3600*SECONDS)
cache.get ('myKey')

Рекомендации по использованию кеша

  • решите, когда использовать кеш. Общая рекомендация - использовать кеш, когда данные часто читаются, но меняются редко. Поскольку кэшированные данные хранятся в энергозависимой памяти, кеш сервер - не идеальное место для хранения данных. Например, при перезапуске кеш-сервера все данные в памяти будут потеряны. Таким образом, важные данные должны быть сохранены в постоянном хранилище.
  • политика устаревания. Хорошая практика реализовать политику устаревания. Когда кешированные данные устаревают, они удаляются из кеша. Когда нет политики устаревания, данные будут храниться в кеше постоянно. Желательно не делать срок истечения слишком коротким, это может привести к частым обращениям к базе данных. Между тем, желательно не увеличивать срок жизни слишком надолго, это может привести к тому, что данные устареют.
  • Согласованность: это включает поддержание хранилища данных и кеша в синхронном состоянии. Несогласованность может случаться из-за того, что операции по изменению данных в хранилище и кеше не находятся в одной транзакции. Когда речь идёт о масштабировании на несколько регионов, поддержка согласованности между хранилищем и кешами - настоящий челлендж. Для подробностей можно ознакомиться с “Scaling Memcache at Facebook”.
  • Политика вытеснения: когда кеш заполнен, любые запросы на добавление элементов в кеш могут вызвать удаление существующих элементов. Это называется вытеснением кеша. Least-recently-used (LRU) - в первую очередь, вытесняется неиспользованный дольше всех. Другие политики вытеснения, такие как Least Frequently Used (LFU) или First in First Out (FIFO) могут быть использованы для соответствия другим требованиям.

Сеть доставки контента (CDN)

CDN - сеть географически распределённых серверов, используемая чтобы доставить статический контент. Сервера CDN кешируют статический контент, такой как изображения, видео, CSS, javascript файлы и т.д. Кеширование динамического контента - относительно новый концепт и остаётся за рамками этой статьи. Оно включает в себя кеширование HTML страниц, которое основано на пути запроса, переменных запроса, куках и заголовках запроса.

Пока фокусируемся на кешировании статического содержимого.

На рисунке показано, как устроен CDN устроен сверху: когда пользователь посещает вебсайт, CDN сервера, ближайшие к пользователю, доставляют статический контент. Интуитивно понятно, чем дальше пользователи от серверов CDN, тем дольше грузится сайт. Например, если CDN сервера в Москве, пользовали из Санкт-Петербурга получат контент быстрее, чем из Владивостока. Рисунок 1-9 - хороший пример, который показывает как CDN помогает улучшить время загрузки.

Рисунок 1-9

На рисунке 1-10 показан процесс работы с CDN

Рисунок 1-10

  1. Пользователь A пытается получить image.png используя URL. URL предоставлен провайдером CDN. Следующие 2 URL к изображениям - примеры для демонстрации, как выглядят на Amazon или Akami CND:
  1. Если на CDN сервере нет image.png в кеше, CDN сервер запрашивает файл с источника, который должен быть веб сервером или онлайн хранилищем, похожим на Amazon S3.
  2. Источник возвращает image.png CDN серверу, включая опциональные HTTP заголовок время жизни (TTL), который описывает, насколько долго кешируется изображение.
  3. CDN кеширует изображение и возвращает его пользователю A. Изображение остаётся кешированным в СDN, пока не истечёт TTL.
  4. Пользователь B отправляет запрос на то же изображение.
  5. Изображение возвращается из кеша, пока TTL не истечёт.

Рекомендации по использованию CDN.

  • Стоимость: CDN запускаются внешними провайдерами и вы будете платить за доставку входящего и исходящего траффика в CDN. Кеширование редко используемого контента не даёт значительных преимуществ, поэтому убедитесь, что не используете CDN для таких ресурсов.
  • установите подходящее время устаревания кеша: для контента, чувствительного ко времени, установка времени устаревания важна. Время устаревания должно быть ни слишком маленьким, ни слишком большим. Если оно будет слишком большим, контент больше не сможет быть свежим. Если будет слишком маленьким, может вызывать постоянный перезапрос содержимого с источника.
  • запасной план к CDN: вы должны подумать, как ваш сайт / приложение справляется с отказом CDN. Если случится временное отключение CDN, клиенты должны определить проблему и запросить ресурсы с источника.
  • Инвалидация файлов: вы можете удалить файл из CND до того как он устарее путём одной из следующих операций: через API используя версионирование объектов, например, добавляя в url номер версии image.png?v=2

Рисунок 1-11 показывает дизайн после добавления CDN и кеша Рисунок 1-11

  1. Статические ресурсы (JS, CSS, images, др.,) больше не доставляются веб-сервером. Они доставляются с CDN для лучшей производительности.
  2. Загрузка базы данных облегчается за счет кеширования данных.

Сервисы без сохранения состояния (stateless)

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

Stateful архитектура

Statefull север и stateless имеют несколько ключевых различий. Statefull server запоминает клиентские данные (state, состояние) от одного запроса к другому. Stateless сервер не хранит информацию о состоянии.

Рисунок 1-12 показывает пример архитектуры без сохранения состояния.

Рисунок 1-12

На рисунке 1-12 сессионные данные и изображение профиля пользователя A сохранены в сервере 1. Для аутентификации пользователя A HTTP запросы должны быть отправлены на сервер 1. Если запрос будет отправлен на другой сервер, например сервер 2, аутентификация не сработает, так как сервер 2 не содержит сессионные данные пользователя A. Аналогично все HTTP запросы от пользователя B должны отправляться на сервер 2. Все запросы от пользователя C должны отправляться на сервер 3.

Проблема в том, что каждый запрос с одного клиента должен быть отправлен на его сервер. Технически это может быть сделано с sticky sessions (“липкими” сессиями) в большинстве балансеров. Тем не менее, это добавляет лишнюю нагрузку. Добавление или удаление серверов становится труднее. Также тру

The issue is that every request from the same client must be routed to the same server. This can be done with sticky sessions in most load balancers [10]; however, this adds the overhead. Adding or removing servers is much more difficult with this approach. Также сложно справиться с отказами серверов.

Stateless архитектура

Рисунок 1-13 показывает stateless архитектуру.

Рисунок 1-13

В stateless архитектуре HTTP запросы от пользователей могут быть отправлены к любому веб-серверу, который получает состояние из общего хранилища данных. Состояние сохраняется в общем пространстве и содержится отдельно от веб серверов. Stateless система проще, более надёжна и масштабируема.

Рисунок 1-14 показывает обновлённый дизайн с приложениями без сохранения состояния.

Рисунок 1-14

На рисунке 1-14 мы перемещаем сессионные данные из веб серверов и сохраняем их в постоянном хранилище. Общие хранилища данных могут быть реляционными базами, Memcached / Redis, NoSQL и другими. Хранилище NoSQL выбирается для упрощения масштабирования. Автомасштабирование означает добавление или удаление серверов, основанное на показателях нагрузки. После того как состояние перемещено из зоны приложений, автомасштабирование приложений достигается легко путём добавления или удаления серверов, основанное на загрузке по траффику.

Ваш вебсайт растёт быстро и привлекает значительное количество международных пользователей. Чтобы улучшить доступность и предоставить лучший пользовательский опыт по всем географическим районам, добавим поддержку нескольких центров обработки данных.

Data centers

Рисунок 1-15 показывает пример настройки с двумя датацентрами. В нормальном режиме пользователи маршрутизированы средствами geoDNS к ближайшему датацентру, с делением траффика x% в US-East и (100 - x)% в US-West. geoDNS - это DNS сервис, который разрешает доменные имена в их IP адреса в соответствии с их местоположением.

Рисунок 1-15

В случае значительного отказа датацентра, мы перенаправим весь траффик к живому датацентру. На рисунке 1-16 датацентра 2 (US-West) оффлайн и 100% траффика маршрутизировано к здоровому дата центру 1 (US-East).

Рисунок 1-16

Несколько технических вызовов должны быть решены для достижения развёртывания на несколько датацентров:

  • Переадресация траффика: для перенаправления траффика в нужный датацентр нужны эффективные инструменты. GeoDNS может быть использован, но мы не можем оперативно управлять DNS на клиенте. Если у вас есть возможность, используйте BGP для анонса нескольких IP адресов.

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

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

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