Читаем Создание микросервисов полностью

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

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

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

Пока одна команда изучала проблемы, возникшие с нижестоящей системой, все остальные приступили к выявлению причин возникновения нештатной ситуации в нашем приложении. Были обнаружены сразу несколько проблем. Для обслуживания нижестоящих подключений мы использовали пул HTTP-соединений. Потоки этого пула имели показатели времени ожидания, настроенные на время ожидания HTTP-вызова, направляемого в адрес нижестоящей системы, что было вполне приемлемо. Проблема заключалась в том, что всем исполнителям при замедлении нижестоящей системы приходилось ожидать истечения лимита времени. Пока они ждали, в пул приходили новые запросы, требовавшие исполнительных потоков. Из-за отсутствия доступных исполнителей эти запросы зависали. Оказалось, что у библиотеки пула соединений, которую мы использовали, была настройка лимита времени ожидания исполнителей, но по умолчанию она была отключена! Это вызвало огромное скопление заблокированных потоков. У нашего приложения в любой момент времени обычно было 40 параллельных соединений. В течение пяти минут возникшая ситуация привела к резкому возрастанию количества соединений до 800, что и обрушило систему.

Хуже того, нижестоящий сервис, с которым шел диалог, обеспечивал менее 5 % функциональных возможностей, используемых нашей клиентской базой, а доля доходов от него была еще меньше. Разобравшись в ситуации, мы пришли к стойкому убеждению, что с системами, просто замедляющими свою работу, справиться намного сложнее, чем с системами, которые быстро выходят из строя. Замедление в распределенных системах имеет убийственный эффект.

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

Антихрупкая организация

В своей книге «Антихрупкость» (Random House) Нассим Талеб (Nassim Taleb) рассказывал о таких вещах, как получение, как бы странно это ни звучало, пользы от сбоев и нештатной работы. Ариэль Цейтлин (Ariel Tseitlin) применительно к тому, как работает Netflix, воспользовался этой концепцией для выработки понятия «антихрупкая организация».

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

Перейти на страницу:

Похожие книги