Redundancy

Redundancy

Введение

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

Приложение популярно

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

Открываем доступ из интернетов

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

Redundancy

Наплыв пользователей

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

Redundancy

Инвестиции

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

Redundancy

Опять падение

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

О нет! Это и был ваш сервер! А у вас уже миллионы пользователей! Пока ваш сервер, что называется, АФК, никакие напоминания вашим пользователям приходить не будут! На следующий день вы смотрите утренний выпуск новостей:

«Массовое забывание сменки школьниками, система образования терпит крах»

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

Redundancy

Redundancy

Как мамин архитектор, строя инфраструктуру вашей системы, вы забыли об одной важной вещи: redundancy. Или “избыточность”.

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

По факту, к этому серверу может даже не быть вообще обращений, пока первый работает. И кажется, что он “простаивает” и крутит счета вам потом за электричество. Но в этом и смысл redundancy: вы жертвуете каким-то количеством излишних ресурсов в моменте, чтобы в случае проблем у вас всегда была рабочая система на другой машине. Если не будет, то убытков вы затем потерпите гораздо больше, чем будут ваши счета за свет.

Redundancy

Redundancy повсюду

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

У вас всегда должно быть в идеале минимум 3 сервера, на которых запущена копия вашего приложения. Плюс, вы можете по ним ведь нагрузку распределять тогда. Но это уже другая история. Тем не менее посыл такой:

Redundancy — важный аспект проектирования любой системы и отдельная статья учёта бизнес-расходов. Всегда помните об этом и поднимайте несколько серверов для ваших приложений!

Single point of failure

Используя подход redundancy, мы пытаемся решить проблему single point of failure или «единственная точка падения». Это когда ваши бравые пользователи-школотроны отправляют запрос в некоторую систему, состоящую из каких-то компонентов, где если хотя бы один из этих компонентов отрубается, то всё приложение в целом перестаёт работать!

Если ваше приложение состоит из одного единственного сервера в принципе, то как бы это и есть ваша единственная точка падения. Если его вырубить, сжечь ему жесткий диск, поймать на него баннер, вирусы-троянские-кони, пожирающие файлы на этом сервере, или просто выключить его из розетки, как и сделала ваша мама, то ВСЁ ваше приложение просто перестаёт работать. Оно же было запущено на этом компе!

Окей, предположим, что вы, например, запустили ваше приложение на одном сервере (компе), а вашу великолепную базу данных (postgres или mysql какой-нибудь) на другом компе и соединили эти два компа проводом (ну или по интернетику). Теперь ваше приложение передаёт все данные в базу, которая как бы на другом сервере. Вроде, надёжнее.

Но на самом деле — без разницы. У вас теперь в приложении целых два single points of failure! Если отвалится сервер с приложением, то пользователи не смогут никуда посылать запрос, потому что они даже не в курсе, что у вас на другом сервере база запущена. Да и не знают они, как в неё там SQL писать, они ж просто кнопки жмут в телефоне, блин!

А если отвалится база на другом сервере, то ваше приложение будет просто всем пользователям постоянно кидать ошибку, говоря, что мол: «Сорян, не могу ничего в БД сохранить, потому что она как бы сдохла. Вы там держитесь, сменку не забывайте, а то держурные в школе остановят, спросят документики и прочее».

Redundancy

Это как бы по сути значит, что ваше приложение лежит! Сохранить-то или получить из него что-нибудь ведь нельзя! Поэтому, чем больше отдельных компонентов в вашей системе, тем больше у вас потенциальных single points of failure. И это всегда нужно учитывать при проектировании вашей инфраструктуры.

Использование копий

Решается всё это точно так же — копированием серверов. Нужно избавиться от single point of failure в виде сервера, где запущено приложение. Так давайте запустим это приложение на еще двух отдельных серверах. У нас получится кластер из трёх серверов, на каждом из которых запущено абсолютно то же самое приложение. Один сервер падает — ещё два остаются доступны.

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

Redundancy

Однако в итоге просто запустить по два таких сервера про запас и не использовать их никак — не очень выгодно. Сами машины стоят денег, крутят свет и всё такое. Было бы здорово, чтобы они не просто простаивали всё время до часа Х, а тоже выполняли какую-то полезную нагрузку.

Поэтому умные люди (которые никогда не забывают сменку) подумали и решили — «А давайте мы все обращения к одному нашему master-серверу будем невидимо для пользователей на самом деле направлять на какой-то случайный сервер в кластере, вместо всё время одного и того же!».

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

Load Balancer

«А как же распределить запросы пользователей по разным серверам?», спросите вы. Ответ — Load Balancer! Или балансировщик нагрузки. Это специальная программа, в которой можно зарегистрировать все имеющиеся у вас серверы. После этого ваши пользователи начинают посылать запросы уже не напрямую на какой-то один из них, а на Load Balancer.

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

Redundancy

Балансировщики нагрузки используются повсеместно, потому что сейчас вся разработка строится через микросервисы. Вообще, микросервисы — очень мощная и глубокая тема, о которой я обязательно расскажу в будущих материалах, это прямо must have. А пока можете почитать мой пост в Телеграм на эту тему.

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

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

Помните! Балансировщик — тоже прога, и она типа на компе каком-то лежит. Это значит, что вы снова можете вызвать гнев школотронов по всему миру, если по какой-то причине комплюхтер, где запущен тот же nginx, вдруг решит отрубить ваш батя, потому что “свет мотает, кошелёк не резиновый”.

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

Redundancy

В этом случае ваш балансировщик нагрузки сам является single point of failure… Если он один! Да-да, эта проблема решается точно так же, как и та, что у нас была раньше. На самом деле вам просто нужно поднять ещё один точно такой же load balancer, который будет на подмене у основного, если тот отвалится. Алгоритм действий тот же самый! Ничего гениального люди не придумали для этого: если ломается один комп, то давайте поставим два, чтоб наверняка.

Redundancy

Проблема состояния реплик

Однако важно помнить об ещё одной вещи, в данном случае. Если в вашем приложении вы храните что-то в оперативной памяти… То эти данные будут доступны только на том компе, на котором это приложение запущено!

Если вы ставите 3 сервера, на которых запущено одно и то же приложение, и каждый из этих серверов сохраняет что-то у себя в памяти, то у каждого эти данные в RAM могут отличаться от других его копий. Чем это грозит вообще — например, если у вас в приложении есть какой-то кэш, который вы держите в RAM для ускорения (хэшмапу там создали, допустим) и храните в нём данные каких-то популярных нотификаций, то этот кэш будет валиден только на одном сервере.

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

На самом деле, это довольно безобидная ситуация. Бывает иначе, например, крутите вы нотификацию в памяти и отправляете её сразу нескольким получателям. При этом ваше приложение гарантирует своим пользователям, что они точно получат это сообщение. Представим, что на конкретном сервере вы успели в цикле отправить эту нотификацию одному пользователю, собираетесь послать второму… БАМ! Сервер падает. Тогда получается, что нотификация исчезает в песках времени, а второй пользователь будет находиться в непрерывном режиме «Хатико» — так себе картина.

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

Когда вы не храните никакого состояния, то ваши сервисы называются stateless (без состояния). И это то, к чему следует стремиться. Вы спросите: «А куда ж мне тада сохранять, если ну нада мне?!». Используйте какое-то внешнее хранилище на отдельных серверах, которые будут общими для всех ваших серверов приложений. Поднимите базу или используйте Redis в качестве кэша, а брокер сообщений — для доставки информации между разными серверами.

Заключение

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

«Если что-то может произойти, то однажды оно произойдет»

В разработке мы всегда имеем дело с большими числами и вероятностями. Если даже вероятность крайне мала и она не ненулевая — однажды она сыграет, не сомневайтесь.

Поэтому одна из важнейших задач для крутых инженеров — убеждаться, что вы учли все потенциальные проблемы, которые могут всплыть в вашей системе. Так вы сможете построить надёжное (reliable) и устойчивое (resilient) приложение, и наконец-то изменить мир, который держится всего-навсего на том, что школьники всё ещё достаточно осознанны, чтобы не забывать утром сменку.

1212
Начать дискуссию