В статье описан подход к построению высоконагруженных распределенных систем, который может быть полезен как reference при подготовке к интервью в FAANG по system design.
Обо мне
Software Engineer с опытом около 7 лет. Успел поработать в стартапах и крупных компаниях. Начинал с разработки прошивок для микроконтроллеров, а сейчас занимаюсь написанием высоконагруженных сервисов. На данный момент работаю в компании Yandex, где мы с командой строим Доставку для вас.
System design. Реальный проект vs Интервью
В реальной жизни стадия проектирования помогает сохранить важные ресурсы: время и деньги. И чтобы ответить на вопрос, готовы ли мы их потратить для построения системы, существует фаза Discovery. На ней продуктовая и техническая команды вместе определяют скоуп и ограничения проекта. На основе собранных требований техническая команда проектирует дизайн системы.
Note: Скоуп - это функциональные и нефункциональные требования; Ограничения - это то, что не будет реализовано в рамках проекта. Функциональные требования описывают поведение приложения, которые ожидает бизнес (expected result). К нефункциональным относятся технические ограничения системы (какую нагрузку оно должно держать, его доступность и прочее).
Обычно в рамках интервью у вас есть 45 минут на то, чтобы продемонстрировать свои навыки (не учитывая времени на знакомство и обсуждения в конце). Когда вы получили задание, не спешите начинать что-то проектировать, т.к нельзя сделать то, не знаю чего.
Дизайн следует разбить на несколько этапов. Каждый из них важен и достоин отдельной статьи, но на собеседовании ваше время ограничено, поэтому советую придерживаться таймингов, например:
Сбор требований (3-4 минуты)
Оценка нагрузки (3-4 минуты)
Высокоуровневый дизайн (10 минут)
Компонентный дизайн (15 минут)
Повышение отзывчивости и отказоустойчивости (5 минут)
Мониторинг и алертинг (2-3 минуты)
Безопасность (1 минута)
Сбор требований
Для начала нужно определиться с тем, что мы собираемся реализовать в рамках проекта (scope): а именно функциональные и нефункциональные требования; и что оставляем за скобками (out of scope). Выделите пару-тройку основных фичей, которые отличают это приложение от остальных. На их основе стоит накидать черновик API, который поможет во время оценки нагрузки.
Note: На интервью не стоит тратить много времени на сбор требований. Но на реальном проекте это один из важнейших этапов.
Оценка нагрузки
Определите, чем нагружено ваше приложение: вычислениями (compute intensive), данными (data intensive) или сетью (network intensive). Это и есть bottleneck сервиса, уделите ему наибольшее внимание при вычислениях.
Например, если стоит задача спроектировать сервис вроде Доставки, то можно рассуждать так:
Сервис будет выполнять работу: создание заказа на доставку, поиск исполнителя (курьера), просмотр и трекинг заказа, оплата.
Тогда у него будет 4 вида пользователей: отправитель, получатель, курьер и администратор.
Отправители создают заявки, получатели их трекают, а курьеры в основном принимают и перевозят заказ.
Админы не создают особой нагрузки по сравнению с остальными пользователями, поэтому их можно опустить.
Основная нагрузка идет на storage и network, потому что:
заявки нужно хранить длительный период (storage),
выполнение заказов нужно трекать (network).
Из этого следует, что в перспективе 5 лет стоит подумать о таких параметрах, как:
Daily active users (DAU) && Monthly active users (MAU).
Read/Write баланс.
Network (трафик, connections, rps).
Storage (RAM, ssd/hard disks).
Стоимость создания инфраструктуры и ее поддержки (опционально).
Посчитав все это, вы сможете понять: будет ли приложение держать нагрузку, грубо говоря, на одной машинке, либо же понадобится кластер, распределенный на несколько ДЦ, чтобы удовлетворять требованиям отказоустойчивости (fault tolerance), низкой задержки (low latency) и консистентности данных (обычно eventual consistency).
Высокоуровневый дизайн
После того, как вы определились с требованиями и нагрузкой, пора переходить к высокоуровневому дизайну системы. На данном этапе нужно:
Вспомнить про оговоренные функциональные требования.
На основе их выделить основные сервисы/компоненты системы.
Определиться с основной БД (обычно выбор стоит между SQL и NoSQL).
Note: Представьте, будто вы разрабатываете систему, которая будет жить на одной машинке. Это позволит вам не думать о масштабировании преждевременно.
Далее в примерах будут использоваться следующие обозначения:
Пример высокоуровнего дизайна
Компонентный дизайн
Дьявол кроется в деталях, поэтому критически важные компоненты системы стоит разбить на более мелкие. Нужно показать, как эти компоненты будут взаимодействовать между собой, какие алгоритмы и структуры данных будут использоваться.
Пример детализации компонента Search UI
Повышение отзывчивости и отказоустойчивости
Теперь настал тот момент, когда стоит задуматься об отзывчивости и отказоустойчивости. Исходя из требований к системе какие-то сервисы (компоненты) будут более высоконагруженными, какие-то менее, поэтому нужно подумать про:
масштабирование,
балансировку нагрузки,
кэширование горячих данных (чаще всего используются),
ограничение нагрузки (rate limiters),
шардирование и репликацию данных,
шину данных (брокеры сообщений).
Пример отказоустойчивого компонента Search UI
Мониторинг и алертинг
Очень часто на собеседованиях забывают упомянуть про мониторинг. Стоит выбирать те метрики, которые нам о чем-то говорят и на которые мы можем повлиять. Их можно разделить на две группы: продуктовые и технические.
Продуктовые метрики специфичны для каждого продукта. Пример: кол-во созданных заказов, MAU, DAU.
Из технических метрик часто мониторят тайминги и rps ендпоинтов, ресурсы машинки (cpu wait, кол-во свободных потоков, память), ресурсы БД (время выполнения запросов, кол-во соединений к базе, лаг репликации), network (кол-во открытых соединений, пропускной канал).
Пример мониторинга в Grafana
Безопасность
Если осталось еще немного времени, то можно коснуться темы безопасности системы. Какие могут осуществляться типы атак/фродов и каким образом мы можем от них защититься. Достаточно упомянуть использование сторонних сервисов (напр. CloudFlare).
Заключение
В данной статье я постарался систематизировать и дать в общем виде подход к решению задач по построению высоконагруженных распределенных систем. Этот план хорошо подходит при прохождении system design интервью.
В следующих статьях разберем конкретные примеры и более детально обсудим их построение. Напишите в комментариях, какие системы Вам хотелось бы рассмотреть. Спасибо!
Полезные ссылки
Пример качественного интервью по system design
Библия для тех, кто хочет научиться разрабатывать высоконагруженные распределенные системы
Книжный клуб Tinkoff
Доклад с Highload про построение Экспресс Доставки в Яндексе
Проходим L6 интервью на system design в FAANG
Архитектурные секции собеседования в Яндексе
Общие принципы прохождения system design интервью