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

Архитектурные меры безопасности

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

Я был техническим руководителем проекта по созданию сайта классифицированной онлайн-рекламы. Сайт обслуживал довольно большие объемы информации и приносил довольно высокий доход. Наше основное приложение занималось выводом на экран классифицированных рекламных объявлений, а также служило прокси-сервером вызовов, осуществляемых по адресам других сервисов, предоставляющих различные виды продукции (рис. 11.1).

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

Рис. 11.1. Сайт классифицированных рекламных объявлений, на котором происходит постепенное избавление от устаревших приложений

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

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

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

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

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

Все книги серии Бестселлеры O'Reilly

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