System Design для начинающих: всё, что вам нужно. Часть 3. system design.. system design. Анализ и проектирование систем.. system design. Анализ и проектирование систем. Микросервисы.. system design. Анализ и проектирование систем. Микросервисы. Распределённые системы.
System Design для начинающих: всё, что вам нужно. Часть 3 - 1

Вам не нужно изучать какую‑либо теорию, кроме этой статьи, чтобы начать собеседоваться. После прочтения смело приступайте к решению типовых System Design задач.

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


Содержание

  1. Зачем изучать проектирование систем?

  2. Что такое сервер?

  3. Задержка и пропускная способность

  4. Масштабирование и его типы
    + Вертикальное
    + Горизонтальное

  5. Автоматическое масштабирование

  6. Оценка на коленке

  7. Теорема CAP

  8. Масштабирование базы данных
    + Индексирование
    + Партиционирование
    + Архитектура «master-slave»
    + Multi-master
    + Шардирование
    + Недостатки Шардирования

  9. SQL и NoSQL СУБД. Когда какую базу данных использовать?
    + SQL СУБД
    + NoSQL СУБД
    + Особенности масштабирования
    + Когда использовать ту или иную базу данных?

  10. Микросервисы
    + Что такое монолит и микросервис?
    + Почему мы разбиваем наше приложение на микросервисы?
    + Когда следует использовать микросервисы?
    + Как клиенты отправляют запросы?

  11. Load Balancer
    + Зачем нам нужен балансировщик нагрузки?
    + Алгоритмы балансировщика нагрузки

  12. Кэширование
    + Введение в кэширование
    + Преимущества кэширования
    + Типы кэшей
    + Подробное описание Redis

  13. Хранилище BLOB-объектов
    + Что такое BLOB и зачем нам нужно хранилище BLOB?
    + AWS S3

  14. Сеть доставки контента (CDN)
    + Знакомство с CDN
    + Как работает CDN?
    + Ключевые понятия в CDN

  15. Message Broker
    + Асинхронное программирование
    + Зачем мы добавили посредника для передачи сообщений?
    + Queue
    + Stream
    + Кейсы использования

  16. Apache Kafka Deep dive
    + Когда использовать Kafka
    + Внутреннее устройство Kafka

  17. Pub/Sub

  18. Event-Driven Архитектура
    + Введение
    + Зачем использовать EDA?
    + Система нотификаций с id
    + Система с передачей всего состояния

  19. Distributed Systems

  20. Leader Election

  21. Big Data Tools

  22. Consistency Deep Dive
    + Когда использовать Strong Consistency, Eventual Consistency
    + Как добиться Strong, Eventual Consistency

  23. Consistent Hashing

  24. Data Redundancy and Data Recovery
    + Зачем мы делаем резервные копии баз данных?
    + Различные способы резервного копирования данных
    + Непрерывное резервное копирование

  25. Proxy
    + Что такое прокси сервер?
    + Прямой и обратный прокси сервер
    + Создание собственного обратного прокси-сервера

  26. Как решить любую проблему, связанную с проектированием системы?

Мы рассмотрели разделы 1-7 в части I. 8-9 в части II. Пришло время Микросервисов, Балансировки и Кэширования. Поехали!

Микросервисы

Что такое Монолит и Микросервис?

Монолит: Все приложение строится как единое целое в монолитной архитектуре. Предположим, вы создаёте приложение для электронной коммерции. В монолите вы создаёте только один сервер и все функции (например, управление пользователями, список товаров, заказ, оплата и т. д.) в одном приложении.

Микросервис: Разбивайте большие приложения на более мелкие, управляемые и независимо развертываемые сервисы.

Пример: вы можете разделить приложение для электронной коммерции на следующие сервисы:

  • Сервис для пользователей

  • Сервис для товаров

  • Сервис для заказов

  • Сервис для платежей

Создайте отдельные серверные приложения для каждой службы.

Почему мы разбиваем наше приложение на микросервисы?

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

  • Гибкость в выборе технологического стека. В монолитной системе весь бэкенд написан на одном языке/технологии. Но в микросервисной архитектуре вы можете писать разные сервисы на разных технологических стеках. Например, вы можете создать сервис для пользователей на NodeJS, а сервис для заказов на Golang.

  • Сбой одного сервиса не обязательно повлияет на другие. В монолитной системе, если одна часть серверной части выйдет из строя, то выйдет из строя всё приложение. Но в микросервисах, если выйдет из строя сервис «Заказ», другие части, такие как сервисы «Пользователь» и «Товар», не пострадают.

Когда следует использовать микросервис?

  • «Микросервисы определяются внутренней структурой организации». Предположим, что в стартапе есть 3 команды, работающие над 3 разными бизнес-функциями. Тогда у него будет 3 микросервиса, и по мере роста числа команд микросервисы будут разделяться.

  • Большинство стартапов начинают с монолитной архитектуры, потому что на начальном этапе только 2–3 человека работают над технической частью, но со временем, когда количество команд увеличивается, они переходят на микросервисы.

  • Если мы хотим избежать единичного сбоя, то также выбираем микросервисы.

Как клиенты выполняют запросы в микросервисной архитектуре?

Каждый микросервис может развёртываться независимо.

Предположим, что user service развернут на компьютере с IP-адресом 192.168.24.32 , product service на 192.168.24.38 и так далее с другими сервисами. Все они развернуты на разных компьютерах. Очень неудобно использовать разные IP-адреса (или доменные имена) для каждого микросервиса. Поэтому мы используем API Gateway для этого.

Клиент выполняет каждый запрос в одной конечной точке шлюза API. Он принимает входящий запрос и перенаправляет его в нужный микросервис.

System Design для начинающих: всё, что вам нужно. Часть 3 - 2

Вы можете масштабировать каждый сервис независимо друг от друга. Предположим, что у сервиса продуктов больше трафика и ему требуется 3 машины, сервису пользователей требуется 2 машины, а сервису платежей достаточно 1 машины. Тогда вы можете сделать и так. Посмотрите на рисунок ниже

System Design для начинающих: всё, что вам нужно. Часть 3 - 3

API Gateway предоставляет и ряд других преимуществ:
— Ограничение скорости
— Кэширование
— Безопасность (аутентификация и авторизация)

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

Зачем нам нужен балансировщик нагрузки?

Как мы видели ранее, при горизонтальном масштабировании, если у нас есть много серверов для обработки запросов, мы не можем предоставить клиенту все IP-адреса машин и позволить клиенту самому выбирать, на каком сервере выполнять запрос.

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

System Design для начинающих: всё, что вам нужно. Часть 3 - 4

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

Алгоритмы балансировки нагрузки

1. Алгоритм циклического перебора (Round Robin)

Как это работает: запросы последовательно распределяются между серверами по кругу.

Предположим, что у нас есть 3 сервера: Сервер-1, Сервер-2 и Сервер-3.
Тогда при циклическом распределении 1-й запрос отправляется на Сервер-1, 2-й запрос — на Сервер-2, 3-й запрос — на Сервер-3, 4-й запрос снова отправляется на Сервер-1, 5-й запрос — на Сервер-2, 6-й запрос — на Сервер-3, 7-й запрос снова отправляется на Сервер-1, 8-й запрос — на Сервер-2 и так далее.

System Design для начинающих: всё, что вам нужно. Часть 3 - 5

Преимущества:

  • Простой в реализации.

  • Работает хорошо, если все серверы имеют одинаковую мощность.

Недостатки:

  • Игнорирует загруженность сервера.

2. Взвешенный циклический перебор (Weighted Round Robin)

Как это работает: аналогично Round Robin, но серверам присваиваются веса в зависимости от их производительности. Серверы с более высоким весом получают больше запросов. На рисунке ниже вы можете увидеть количество запросов, чтобы понять, как это работает. На рисунке ниже 3-й сервер больше (имеет больше оперативной памяти, хранилища и т. д.). Таким образом, он получает в два раза больше запросов, чем 1-й и 2-й.

System Design для начинающих: всё, что вам нужно. Часть 3 - 6

Преимущества:

  • Лучше справляется с серверами с неодинаковой мощностью.

Недостатки:

  • Статические веса могут не отражать производительность сервера в реальном времени.

3. Алгоритм наименьшего количества соединений (Least Connections)

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

Преимущества:

  • Балансирует нагрузку динамически в зависимости от активности сервера в реальном времени.

Недостатки:

  • Может плохо работать с серверами, обрабатывающими соединения разной продолжительности.

4. Алгоритм, основанный на хэше (Hash-Based Algorithm)

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

Преимущества:

  • Полезно для поддержания постоянства сеанса.

Недостатки:

  • Изменения на сервере (например, добавление/удаление серверов) могут нарушить согласованность хеширования и сеансов.

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

Кэширование

Введение в кэширование

Кэширование — это процесс сохранения часто используемых данных на высокоскоростном уровне хранения, чтобы будущие запросы к этим данным обрабатывались быстрее.

Пример: предположим, что для получения данных из базы данных MongoDB требуется 500 мс, затем требуется 100 мс, чтобы выполнить некоторые вычисления с этими данными на сервере и, наконец, отправить их клиенту. Таким образом, в общей сложности клиенту требуется 600 мс, чтобы получить данные. Если мы кэшируем эти вычисленные данные и сохраняем их в быстром хранилище, например в Redis, и получаем их оттуда, то мы можем сократить время с 600 мс до 60 мс. (Это гипотетические цифры).

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

Пример: Возьмем в качестве примера сайт с блогами. Когда мы переходим по маршруту /blogs, мы получаем все блоги. Если пользователь переходит по этому маршруту в первый раз, то в кэше нет данных, поэтому нам нужно получить данные из базы данных, и предположим, что время отклика составляет 800 мс. Теперь мы сохранили эти данные в Redis. В следующий раз, когда пользователь перейдет по этому же адресу, он получит данные из Redis, а не из базы данных. Время отклика составляет 20 мс. Когда добавляется новый блог, мы должны каким-то образом удалить старое значение блогов из Redis и заменить его новым. Это называется удалением из кэша. Существует множество способов удаления из кэша. Мы можем установить срок действия (Time to live — TTL); каждые 24 часа Redis будет удалять блоги, и когда запрос поступит от любого пользователя в первый раз после 24 часов, он получит данные из БД. После этого они будут кэшироваться для следующих запросов.

Преимущества кэширования:

  • Улучшенная производительность: Сокращает время ожидания для конечных пользователей.

  • Сниженная нагрузка: разгружает внутренние базы данных и службы.

  • Экономическая эффективность: Снижает сетевые и вычислительные затраты.

  • Масштабируемость: позволяет лучше справляться с большими нагрузками.

Типы кэшей

  1. Кэш на стороне клиента:
    + Хранится на устройстве пользователя (например, в кэше браузера)
    + Сокращает количество запросов к серверу, загрузку сетевого канала
    + Примеры: файлы HTML, CSS, JavaScript.

  2. Кэш на стороне сервера:
    Хранится на сервере
    + Примеры: кэши в оперативной памяти, такие как Redis или Memcached.

  3. Кэш CDN:
    Используется для доставки статического контента (файлов HTML, CSS, PNG, MP4 и т. д.)
    + Кэшируется на географически распределённых серверах
    + Примеры: AWS CloudFront, Cloudflare CDN

  4. Кэш-память на уровне приложения:
    Встроена в код приложения
    + Кэширует промежуточные результаты или результаты запросов к базе данных.

Это было лишь введение. Мы подробно рассмотрим каждый тип кэша.

Погружение в Redis

Redis – это хранилище структур данных в памяти.

В оперативной памяти данные хранятся в ОЗУ. И если у вас есть базовые знания в области компьютерных наук, то вы знаете, что чтение и запись данных из ОЗУ происходит очень быстро по сравнению с диском.

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

Базы данных используют диски для хранения данных. И чтение/запись выполняются очень медленно по сравнению с Redis.

Один из вопросов, который может возникнуть у вас, заключается в том, что если Redis такой быстрый, то зачем использовать базу данных? Разве мы не можем положиться на Redis для хранения всех данных?
Ответ: Redis хранит данные в оперативной памяти, а в оперативной памяти очень мало памяти по сравнению с диском. Если вы решали задачи в leetcode или codeforces, то иногда вы могли бы получать сообщение “Превышен лимит памяти”. Точно так же, если мы храним слишком много данных в Redis, это может привести к ошибке нехватки памяти.

Redis хранит данные в парах «ключ-значение». В базе данных мы получаем доступ к данным из таблицы так же, как и в Redis: мы получаем доступ к данным по ключам.

Значения могут быть любого типа данных. Например – строка, список и т.д:

System Design для начинающих: всё, что вам нужно. Часть 3 - 7

Существуют и другие типы данных, но в основном используются вышеперечисленные.

Я расскажу обо всём, что касается Redis в командной строке, но вы можете настроить всё необходимое в любом приложении, например в NodeJS, Springboot и Go.

Выполните приведенную ниже команду, чтобы установить и запустить Redis на вашем локальном ноутбуке.

docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

Соглашение об именовании ключей в Redis

Вы можете давать ключам любые имена, но, как правило, в разных отраслях используется следующий подход:
— Ключ для пользователя с идентификатором 1 будет называться «user:1»
— Ключ для электронной почты пользователя с идентификатором 2 будет называться «user:2:email»

Используйте “:” для разделения объектов

Теперь давайте обсудим каждый тип данных

  1. Строка

  • SET значение ключа: устанавливает ключ с определенным значением.

  • GET ключ: извлекает значение, связанное с ключом.

  • SET значение ключа NX: сохраняет строковое значение только в том случае, если ключ ещё не существует

  • MGET key1 key2 … keyN: извлекает несколько строковых значений за одну операцию

System Design для начинающих: всё, что вам нужно. Часть 3 - 8

2. Список

  • LPUSH: добавляет значение левее

  • RPUSH: добавляет значение правее

  • LLEN: возвращает длину списка

  • LPOP: извлекает левое значение и возвращает его

  • RPOP: извлекает правое значение и возвращает его.

System Design для начинающих: всё, что вам нужно. Часть 3 - 9

Чтобы создать очередь, нужно только выполнять операции LPUSH и RPOP, то есть добавлять элементы слева и удалять справа (FIFO).

Чтобы создать стек, используйте только LPUSH и LPOP, то есть выталкивайте и заталкивайте элементы с одной стороны (LIFO).

Попробуйте самостоятельно изучить другие команды и типы данных из документации Redis.

Ниже приведено базовое использование NodeJS

Не стесняйтесь попробовать его на предпочитаемом вами языке бэкенда, например Django, Go и т. д.

System Design для начинающих: всё, что вам нужно. Часть 3 - 10

Я написал код для того же примера блога. Если /blog был вызван в первый раз, то данные будут получены из apiCall или базы данных. Но после этого они кэшируются и обслуживаются из Redis. Данные в Redis действительны в течение 24 часов. После этого они автоматически удаляются из Redis.

System Design для начинающих: всё, что вам нужно. Часть 3 - 11

Cash Hit означает, что данные присутствуют в кэше.
Cash Miss означает, что данных в кэше нет

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


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

Меня зовут Невзоров Владимир. Работаю старшим backend разработчиком на HighLoad проекте с порядком пиковой нагрузки в миллион rps. Приветствую) Веду телеграмм канал по Архитектуре, System Design, Highload бэкэнду.

На канале провожу архитектурные каты, публикую полезные материалы, делюсь опытом. Сейчас с участниками канала разбираем книгу Мартина Клеппмана “Высоконагруженные приложения” на стримах(youtube запись).

Для пополнения багажа знаний по теме заходите на мой канал System Design World <=

Материал для подготовки к System Design интервью в виде чек листов на моём boosty. Смотреть.

Успехов в дальнейшем изучение темы System Design!

Автор: avovana7

Источник

Rambler's Top100