• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Гостевая статья Масштабирование до 100 тыс. пользователей

Мы собираемся принять на нашем веб-сайте для обмена фотографиями, Graminsta, от 1 до 100 000 пользователей.

1 пользователь: 1 машина
Почти каждое приложение, будь то веб-сайт или мобильное приложение, имеет три ключевых компонента: API, базу данных и клиент (обычно приложение или веб-сайт). База данных хранит постоянные данные. API обслуживает запросы для этих данных и вокруг них. Клиент передает эти данные пользователю.
В современной разработке приложений я обнаружил, что представление о клиенте как об совершенно отдельной сущности от API значительно упрощает масштабирование приложения.
Когда мы только начинаем создавать приложение, все эти три вещи могут работать на одном сервере. В некотором смысле это напоминает нашу среду разработки: один инженер запускает базу данных, API и клиент на одном компьютере.
Теоретически мы могли бы развернуть это в облаке на одном экземпляре DigitalOcean Droplet или AWS EC2, как показано ниже:
Screen_Shot_2020-01-21_at_8.11.27_AM.png

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

10 пользователей: разделить слой базы данных
Разделение базы данных на управляемом сервисе, таком как Amazon RDS или управляемая база данных Digital Ocean, будет служить нам долгое время. Это немного дороже, чем самостоятельное размещение на одном компьютере или в экземпляре EC2, но с этими услугами вы получаете множество простых дополнений из коробки, которые пригодятся в дальнейшем: многозональная избыточность, реплики чтения, автоматизированные резервные копии и многое другое.

Вот как выглядит система Graminsta:
Screen_Shot_2020-01-21_at_8.13.17_AM.png

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

Вот почему мне нравится думать о клиенте отдельно от API. Это позволяет легко рассуждать о построении для нескольких платформ: веб, мобильной сети, iOS, Android, настольных приложений, сторонних сервисов и т. Д. Все они просто клиенты, использующие один и тот же API.

Аналогичным образом, самая большая обратная связь, которую мы получаем от пользователей, заключается в том, что они хотят использовать Graminsta на своих телефонах. Итак, мы собираемся запустить мобильное приложение, пока мы на нем.

Вот как выглядит система сейчас:
Screen_Shot_2020-02-03_at_9.56.51_PM.png

1 000 пользователей: добавьте балансировщик нагрузки.
Файлы накапливаются в Graminsta. Пользователи загружают фотографии на влево и на право. Мы начинаем получать больше регистраций. У нашего одинокого экземпляра API возникают проблемы с отслеживанием всего трафика. Нам нужно больше вычислительной мощности!

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

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

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

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

Благодаря балансировщику нагрузки наш уровень API может масштабироваться практически до бесконечности, мы будем просто добавлять экземпляры по мере получения большего количества запросов.
Screen_Shot_2020-01-21_at_8.25.50_AM.png

Примечание: на данный момент то, что у нас есть, очень похоже на то, что PaaS-компании, такие как Heroku или AWS Elastic Beanstalk, предоставляют «из коробки» (и почему они так популярны). Heroku размещает базу данных на отдельном хосте, управляет балансировкой нагрузки с помощью автоматического масштабирования и позволяет размещать веб-клиент отдельно от API. Это отличная причина, чтобы использовать такой сервис, как Heroku, для проектов или стартапов на ранних стадиях - все необходимые основы выходят из коробки.
10000 пользователей: CDN
Обслуживание и загрузка всех этих изображений начинает слишком загружать наши серверы.

На этом этапе мы должны использовать службу облачного хранения для размещения статического контента: подумайте об изображениях, видео и многом другом (AWS S3 или Digital Ocean's Spaces). В общем, наш API должен избегать обработки таких вещей, как показ изображений и загрузка изображений.

Еще одна вещь, которую мы получаем из службы облачного хранения, - это CDN (в AWS это надстройка под названием Cloudfront, но многие службы облачного хранения предлагают ее «из коробки»). CDN автоматически кэширует наши изображения в разных центрах обработки данных по всему миру.

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

Screen_Shot_2020-01-21_at_8.30.06_AM.png

100 000 пользователей: масштабирование уровня данных
CDN нам очень помог. Знаменитость YouTube, Mavid Mobrick, только что зарегистрировалась и разместила нас в своей истории. ЦП и использование памяти API низки по всем направлениям - благодаря нашему балансировщику нагрузки, добавляющему 10 экземпляров API в среду - но мы начинаем получать много тайм-аутов на запросы… почему?

После некоторого копания мы видим это: ЦП базы данных колеблется на 80-90%.

Масштабирование слоя данных, вероятно, самая сложная часть уравнения. В то время как для серверов API, обслуживающих запросы без сохранения состояния, мы можем просто добавить больше экземпляров, но с большинством систем баз данных это не так . В этом случае мы собираемся исследовать популярные системы реляционных баз данных (PostgreSQL, MySQL и т. Д.).

Кэширование
Один из самых простых способов получить больше от нашей базы данных - это ввести в систему новый компонент: уровень кэша. Наиболее распространенный способ реализации кэша - использование хранилища ключей в памяти, такого как Redis или Memcached. Большинство облаков имеют управляемую версию этих сервисов: Elasticache в AWS и Memorystore в Google Cloud.

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

Например, в Graminsta каждый раз, когда кто-то заходит на страницу профиля Mavid Mobrick, уровень API запрашивает информацию о профиле Mavid Mobrick из базы данных. Это происходит снова и снова. Поскольку данные профиля Mavid Mobrick не меняются при каждом запросе, эта информация является хорошим кандидатом для кеширования.

Мы будем кешировать результат из базы данных в Redis под ключ user:id со сроком действия 30 секунд. Теперь, когда кто-то заходит в профиль Mavid Mobrick, мы сначала проверяем Redis и просто предоставляем данные прямо из Redis, если они существуют. Несмотря на то, что Mavid Mobrick является самым популярным на сайте, запрос профиля практически не загружает нашу базу данных.

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

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

Читать Реплики
Еще одна вещь, которую мы можем сделать сейчас, когда наша база данных начала поражаться, - это добавить реплики чтения с помощью нашей системы управления базами данных. С помощью описанных выше управляемых сервисов это можно сделать одним щелчком мыши. Реплика чтения останется в актуальном состоянии с вашей главной БД и сможет использоваться для операторов SELECT.

Вот наша система сейчас:
Screen_Shot_2020-01-21_at_8.35.01_AM.png

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

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

Мы хотим убедиться, что у нас установлен мониторинг с использованием таких сервисов, как New Relic или Datadog. Это гарантирует, что мы понимаем, какие запросы выполняются медленно и где необходимо внести улучшения. По мере масштабирования мы хотим сосредоточиться на поиске узких мест и их устранении - часто используя некоторые идеи из предыдущих разделов.

Ссылки:
Этот пост был вдохновлен одним из . Я хотел бы немного подробнее проработать статью на ранних этапах и сделать ее более независимой от облаков. Определенно проверьте, если вы заинтересованы в таких вещах.

Сноски
  1. Несмотря на схожесть с точки зрения того, что мы можем распределить нагрузку по нескольким экземплярам, базовая реализация Redis Cluster сильно отличается от балансировки нагрузки.
Источник:
 
  • Нравится
Реакции: GunQuest
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!