Высоконагруженные системы. Глава 1. Надежные, масштабируемые и удобные в сопровождении приложения
Tags: highload, книги, клеппман, высоконагруженныеприложения
Published: 2023-11-04 19:25:42

Приложения могут быть высоконагруженными данными и высоконагруженными вычислениями. 

Приложения, высоконагруженные данными, строятся из стандартных блоков:

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

Если вкратце, то есть 3 наиболее важных вопроса, которые необходимо рассмотреть при проектировании высоконагруженного приложения:

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

Аппаратные (выход из строя плашки ОЗУ или диска). Выход - использование избыточного кол-ва аппаратного обеспечения, чтобы не наступил уже отказ всей системы.
Программные (провайдер перестал отвечать, процесс исчерпал ресурс). Выход - изначальное продумывание возможных проблем и путей их недопущение, использование средств для ручного мониторинга и алертинга. Автоматический перезапуск системы после фатального сбоя. 
Человеческий фактор (неправильная настройка приложения или необработанная ошибка при каком-нибудь пользовательском сценарии). Выход - проектирование с учетом того, чтобы правильные действия было легко сделать, а неправильные - сложно. Грамотно спроектированное API. Зрелый CD (Continious Delivery), когда есть возможность быстрого отката изменения сервиса или конфига и канареечные релизы. Настроить телеметрию приложения для обнаружения проблем на ранней стадии до массового недовольства потребителей.
Масштабируемость
Масштабируемость - способность системы справлять с возрастающими нагрузками. Эта способность измеряется ответами на следующие вопросы:

Насколько сильные изменения нужно произвести с системой, чтобы она стабильно выдерживала в 10 раз большую нагрузку
Каким образом можно увеличить вычислительные ресурсы чтобы системы выдерживала в 10 раз большую нагрузку.
У нагрузки есть параметры, тип которых разнится в зависимости от устройства системы. Это может быть RPS (кол-во запросов в секунду), кол-во одновременных пользователей, делающих что-то, отношение кол-ва операций чтения к кол-ву операций записи в БД.

Пример с Twitter
Например, рассмотрим архитектуру Twitter. Там есть 2 основных действия - публикация твита и просмотр ленты. В данном случае сложность в масштабировании представляет сильная степень разветвления (пользователь подписан на многих и сам имеет много подписчиков). Это и есть параметр масштабирования.

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

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

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



### Описание производительности системы
После описания нагрузки нужно описать производительность. Для этого нужно ответить на 2 вопроса:

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


![Здесь по оси x - время обработки запроса, а по оси y - кол-во запросов, выполнившихся в определенном коридоре времени](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/piqg11siu5sn7b2bdcnq.png)

Здесь по оси x - время обработки запроса, а по оси y - кол-во запросов, выполнившихся в определенном коридоре времени.

Такие значения обычно прописываются в требованиях к уровню предоставления сервис (SLA), что 99% запросов должны выполняться меньше 200мс, а 50% - менее 100мс.

### Как справиться с нагрузкой
Есть 2 варианта:

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



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