Мои контакты


понедельник, 15 июня 2015 г.

Проектирование высоконагруженных приложений. Часть 1. Начало.

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

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

Начну с самого начала.



Подходы к построению веб-приложений.


Монолитные приложения.


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

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

Сервис-ориентированные приложения.


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

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

Один из главных плюсов сервис-ориентированного подхода — возможность без проблем вести распределенную разработку. Речь не о географической распределенности. Я говорю про ситуацию, когда разработку определенной части системы можно отдать другой команде или на аутсорс. Просто сказать: "Есть такая-то задача, API должен быть такой". Весь остальной проект продолжает жить как жил раньше, а отдельная команда делает новый сервис. Кстати, обычно так проект и развивается. Берется монолитное приложение и постепенно разбивается на отдельные сервисы, каждый из которых берет на себя строго одну задачу. 

Теперь давайте перейдем к понятию масштабирования архитектуры. Существует два типа масштабирования — вертикальное и горизонтальное.

Масштабирование архитектуры.


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


Вертикальное масштабирование заключается в увеличении производительности системы за счет увеличения мощности сервера. Всё. По сути, задача увеличения производительности ложится на плечи производителей железа. Заменили железо на более мощное — система начала работать быстрее.


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

Горизонтальное масштабирование.


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


Конечно, ограничения есть и в этом подходе (нет ничего идеального :)). Иногда даже употребляют термин "диагональное" масштабирование, оно предполагает совмещение этих двух подходов, т.е. вы одновременно покупаете новое железо и активно переписываете приложение. Примерно так построена работа во всем известном Stack Overflow.

Вообще, чтобы правильно выполнять любое масштабирование, нужно хорошо понимать предметную область проекта: как движутся данные внутри системы, как они обрабатываются, откуда и куда переходят. Иногда можно заметить, что некоторые данные всегда должны быть актуальными, а на другие можно на время "забить" и показывать обновления не сразу. 

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

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

Трехзвенная архитектура.


Нагруженные распределенные системы, как правило, имеют три звена — frontend, backend и storage (хранение данных). У каждого звена свои функции и оно по своему масштабируется. Наличие такой архитектуры серверов уже говорит о том, что система — высоконагруженная.

Изначально запрос от пользователя приходит на frontend. Frontend-серверы обычно отвечают за отдачу статики. Дальше запрос идет на backend-сервер, где происходят все нужные вычисления и сборка страницы. Если нужно, то backend взаимодействует с третим — хранилищем. Вообще, frontend-сервер — это, как правило, такой легкий и быстрый веб-сервер (обычно nginx), который не занимается вычислениями, и если может, то сразу отдает клиенту ответ, либо проксирует его на backend.


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