Сторонний сервис не отвечает: как не дать одной интеграции остановить весь сайт
Circuit breaker — паттерн, который временно ограничивает обращения к неисправной зависимости и помогает не распространить её отказ на остальные функции сайта. Если расчёт доставки перестал отвечать, приложение не должно бесконечно ждать его на каждом запросе, занимая все доступные рабочие процессы. Оно должно перейти к заранее определённому поведению и контролируемо проверить восстановление.
Для владельца сайта важен результат: посетитель может продолжить допустимое действие, заявка не теряется, а интерфейс не обещает того, что система сейчас выполнить не способна. Установка библиотеки сама по себе этого не обеспечивает. Нужно согласовать границы ожидания, обработку данных и понятный сценарий для клиента.
Разберём, как защитить сайт при отказе внешнего сервиса: чем отличаются тайм-аут, повтор и circuit breaker, когда допустим запасной вариант и как проверить возвращение интеграции к работе. Числа ниже используются только в учебных примерах; настройки выбирают по поведению конкретного приложения и последствиям ошибок.
Почему одна интеграция может остановить весь сайт
Представим магазин, в котором сервер запрашивает внешние рекомендации при открытии карточки товара. Обычно ответ быстрый. Во время сбоя запросы ждут окончания тайм-аута, а следующие посетители создают новые ожидания. В результате ресурсы, нужные для каталога и корзины, оказываются заняты необязательной функцией.
Похожая цепочка возникает с проверкой адреса, CRM, поиском и отправкой уведомлений. Опасен не только явный отказ. Медленный ответ может удерживать соединение или рабочий процесс дольше, чем мгновенная ошибка. Поэтому вопрос «зависимость иногда отвечает» ещё не объясняет, способен ли сайт обслуживать основной поток.
Сначала выясните, какая функция вызывает интеграцию, сколько ресурсов занимает ожидание и нужен ли её результат прямо сейчас. Для сохранения заявки обязательна надёжная запись обращения, а немедленное появление этой записи в CRM может быть отдельным этапом. Такое разделение допустимо только при реализованной отложенной обработке и контроле её завершения.
Не начинайте с общего выключателя для всех внешних вызовов. Если ошибка рекомендаций одновременно отключит оплату и отправку заявок, защита расширит последствия сбоя. Границу выбирают вокруг конкретного вида операций и общих для него причин отказа.
Для приложения из нескольких экземпляров отдельно определите область состояния: каждый экземпляр оценивает собственные вызовы или используется общий механизм решения. Локальные состояния могут различаться, например при проблеме сети только у одной части приложения. Это не обязательно ошибка. Общий механизм, в свою очередь, требует доступности и понятного поведения при потере связи с ним. Не объединяйте наблюдения разных маршрутов лишь ради одинакового цвета на панели. В инструкции укажите, к какому экземпляру, направлению и типу операции относится сигнал; тогда ответственный сможет отличить локальную проблему от общего отказа поставщика.
Четыре механизма, которые решают разные задачи
Устойчивость интеграций обычно складывается из нескольких ограничений. Каждый механизм отвечает на свой вопрос: как долго ждать, допустима ли ещё одна попытка, стоит ли вообще обращаться к зависимости и сколько ресурсов разрешено занимать одновременно.
| Механизм | Что ограничивает | Чего сам по себе не подтверждает |
|---|---|---|
| Тайм-аут | Время ожидания конкретного действия | Что удалённая операция не выполнилась |
| Retry, повтор | Число и расписание новых попыток | Что повтор безопасен для данных |
| Circuit breaker | Допуск новых вызовов при наблюдаемом отказе | Что уже начатые вызовы остановлены |
| Bulkhead, разделение ресурсов | Влияние одной группы операций на другие | Что внешний сервис исправен |
В описании Retry в Microsoft Learn повторы рассматриваются как способ пережить временный сбой; выбор зависит от типа ошибки, времени и допустимости повторного действия. Если зависимость устойчиво недоступна, постоянное повторение может только увеличить нагрузку. Circuit breaker нужен для ограничения таких обращений.
Запасное поведение, или fallback, определяет приложение. Это может быть ручной ввод адреса, скрытый блок рекомендаций, отложенная передача или понятный отказ в операции. Ни один технический механизм не выбирает корректное обещание клиенту вместо владельца процесса.
Как работают Closed, Open и Half-Open
В описании Circuit Breaker Microsoft используются три основных состояния. Их названия удобнее понимать через разрешённые действия, а не переводить буквально как «сайт открыт» или «сайт закрыт».
| Состояние | Поведение вызовов | Следующий шаг |
|---|---|---|
| Closed | Обращения проходят, результаты учитываются | При выполнении правила отказов включается ограничение |
| Open | Новые защищённые вызовы быстро отклоняются | После предусмотренного интервала допускается проверка восстановления |
| Half-Open | Проходит ограниченное число пробных обращений | По их результатам ограничение снимается либо возобновляется |
Состояние Open не означает, что весь сайт должен возвращать ошибку. Приложение получает известный результат отказа от вызова и выполняет согласованный сценарий. Например, каталог открывается без рекомендаций. Но если действие нельзя корректно выполнить без зависимости, посетитель должен получить честное сообщение об ограничении.
Переход после интервала ожидания зависит от реализации: его может запускать очередное обращение, а не отдельный таймер. Поэтому отсутствие трафика не всегда приводит к активной проверке сервиса. Уточните это поведение до настройки ожиданий от панели наблюдения.
Какие ошибки учитывать и как задать порог
Сначала определите единицу наблюдения: отдельная попытка HTTP-запроса или целая операция после разрешённых повторов. Если два слоя считают разные вещи, одинаковая надпись «доля ошибок» будет означать разный риск. Зафиксируйте также окно, минимальное число наблюдений и перечень ошибок.
Например, документация Resilience4j различает окна по времени и количеству вызовов, минимальный объём данных, долю неуспешных и долю медленных обращений. Это возможности конкретной реализации; значения и условия переходов нужно проверять в используемой библиотеке.
В учебной политике можно учитывать последние двадцать завершённых подходящих вызовов и открывать ограничитель при десяти ошибках или больше. Девять из двадцати дают 45%, десять — 50%. До накопления двадцати результатов эта конкретная политика не принимает решение по доле. На малом потоке такой порог может реагировать слишком поздно, поэтому его нельзя переносить в проект без оценки.
Не смешивайте все отрицательные результаты. Ошибка соединения, тайм-аут и серверный отказ отличаются от неверно заполненного поля. Ограничение частоты, истёкший ключ и несовместимый формат ответа также требуют собственных решений. Последние проблемы могут нуждаться в исправлении настройки или кода, а не в ожидании восстановления поставщика.
Успешный HTTP-статус не отменяет проверки содержимого. Если расчёт должен содержать стоимость и валюту, пустой объект нельзя использовать как корректный результат. При этом ошибку данных одной операции не следует без объяснения превращать в отключение всех возможностей общей интеграции.
Как связать тайм-ауты, повторы и время ожидания посетителя
Задайте общий предел времени для пользовательского действия, затем распределите его между локальной обработкой и допустимыми внешними вызовами. Отдельно учитывайте установление соединения, получение ответа и ожидание свободного ресурса. Проверьте, что именно охватывает настройка выбранного клиента: название параметра не гарантирует одинакового поведения библиотек.
В материале AWS о тайм-аутах и повторах объясняется, как повторные запросы увеличивают нагрузку и почему случайная добавка к задержке, jitter, помогает распределить попытки во времени. Важен и общий бюджет: число повторов без ограниченного времени не защищает от слишком долгого ожидания.
Учебный расчёт: две последовательные попытки по 800 миллисекунд и пауза 200 миллисекунд уже занимают до 1,8 секунды, без учёта остальной работы. Если весь путь должен завершаться за две секунды, запас составляет лишь 200 миллисекунд. Это ещё не доказывает, что путь уложится в срок: нужны измерения и проверка всех этапов.
Определите один ответственный уровень повторов. Если браузер, сервер и клиентская библиотека независимо повторяют действие, количество обращений может неожиданно вырасти. После отказа circuit breaker не нужно немедленно запускать ту же попытку в цикле. Поведение должно учитывать состояние ограничения и оставшееся время.
Окончание ожидания не отменяет возможный результат на стороне поставщика. Особенно это важно для записи данных, платежа и бронирования. До повтора выясните, как операция идентифицируется и где можно проверить её состояние.
Как выбрать безопасный fallback для конкретной функции
Запасное поведение должно сохранять смысл действия. Уберите необязательные рекомендации, предложите ручной ввод вместо подсказки адреса или сохраните обращение для последующей передачи. Последний вариант подходит, только если обращение действительно записано, назначен ответственный и существует проверяемый механизм дальнейшей обработки.
| Зависимость | Возможный сценарий | Что требуется подтвердить |
|---|---|---|
| Подсказки адреса | Ручное заполнение полей | Проверка и сохранение введённого адреса работают без сервиса подсказок |
| Персональные рекомендации | Страница без дополнительного блока | Товар, цена и основная навигация остаются доступны |
| Передача в CRM | Отложенная отправка сохранённого обращения | Есть контроль задержки и защита от повторного создания |
| Точный расчёт доставки | Согласованный запрос на уточнение условий | Покупатель понимает, что окончательная стоимость пока не подтверждена |
Не подменяйте неизвестную цену нулём и не считайте отсутствие ответа подтверждением наличия товара. Если используются ранее сохранённые сведения, задайте допустимую давность и область применения. Устаревшая рекомендация и устаревшая стоимость заказа имеют разные последствия.
Запасной путь тоже может иметь зависимости. Ручной ввод не поможет, если сохранение всё равно требует ответа того же внешнего API. Отложенная передача не спасёт заявку, если единственная очередь недоступна. Проверяйте весь выбранный маршрут и определяйте понятный отказ, когда безопасного продолжения нет.
Как не дать ожиданиям занять все ресурсы
Circuit breaker принимает решение по наблюдениям и не обязательно успевает остановить первый большой поток зависших обращений. Уже начатые операции могут продолжать занимать ресурсы. Поэтому отдельно ограничивают количество одновременных вызовов, очередь ожидающих и время нахождения в ней.
В описании Bulkhead Microsoft рассматривается разделение ресурсов по группам операций. Для сайта это может означать отдельные возможности обработки уведомлений и оформления заказов, чтобы задержка одного направления не занимала всё приложение.
Зафиксируйте предел очереди и действие при его достижении. Бесконечная очередь переносит проблему на память и время ожидания. Для необязательного расчёта разумным результатом может быть быстрый переход к согласованному варианту; для важного задания требуется надёжное сохранение и понятный срок обработки.
Учитывайте число экземпляров приложения. Ограничение «десять одновременных запросов» на каждом из пяти экземпляров допускает до пятидесяти таких запросов суммарно. Проверяйте, соответствует ли это возможностям поставщика и общей инфраструктуры. Локальное ограничение не становится общим автоматически.
Что делать с операцией, результат которой неизвестен
Представим, что сайт отправил запрос на создание заказа во внешней системе, но ответ потерялся. Переключение circuit breaker в Open не сообщает, был ли заказ создан. Если приложение сразу отправит новое независимое действие через запасного поставщика, возможен повторный эффект.
Для таких операций нужен устойчивый идентификатор, предусмотренная поставщиком защита от повторов и сверка состояния. Пока результат неизвестен, храните именно это состояние. Посетителю можно показать, что операция проверяется, если у бизнеса действительно есть процедура её завершения и контакт для уточнения.
Переход в Half-Open не делает повтор записи безопасным: пробный вызов тоже способен создать заказ или списание. Если API поддерживает ключ идемпотентности, повторы одной логической операции используют тот же ключ по правилам поставщика. Смена ключа после тайм-аута может создать новую операцию.
Не превращайте fallback в автоматическое подтверждение успеха. Если запрос не был принят или его состояние невозможно надёжно установить, приложение должно сохранить нужные диагностические сведения и направить случай по согласованной процедуре. Возврат доступности зависимости позже не разрешает слепо повторить все старые операции.
Учебный пример: отказ сервиса подсказок адреса
Допустим, магазин использует подсказки при заполнении доставки, а обычные текстовые поля могут работать самостоятельно. Команда заранее проверила, что ручной адрес сохраняется и не блокирует последующие допустимые шаги. Это подходящий пример ограниченного функционирования без выдуманной стоимости или обещания недоступной доставки.
- Начальный сбой. Несколько обращений заканчиваются по тайм-ауту. Для каждого конкретного запроса интерфейс предлагает ручной ввод.
- Включение ограничения. Когда достигается согласованный порог, новые вызовы подсказок не отправляются; приложение сразу использует подготовленный вариант.
- Период ожидания. Команда видит состояние зависимости и долю посетителей, которым понадобился ручной ввод.
- Проверка восстановления. Разрешается ограниченное число пробных вызовов с собственными тайм-аутами.
- Возврат функции. После выполнения критерия восстановления подсказки снова доступны; уже введённые адреса не затираются.
Предположим, для учебной схемы выбраны одна одновременная проба и три последовательных успешных результата перед возвратом. При неудачной пробе ограничение включается снова. Это описание желаемой политики, а не универсальный набор параметров любой библиотеки. Реализация должна поддерживать именно согласованные условия.
Пробы должны проверять нужную операцию. Ответ служебного адреса поставщика не всегда доказывает работоспособность поиска адреса с вашими правами и форматом запроса. При нескольких экземплярах приложения ограничьте совокупный поток проб, чтобы возвращающаяся система не получила резкий всплеск обращений.
Что наблюдать во время ограниченного функционирования
Следите отдельно за состоянием зависимости, работой защиты и результатом посетителя. Открытый circuit breaker означает, что выбранный маршрут ограничен. Это полезное событие, но оно само по себе не описывает, сколько заявок принято и какие функции недоступны.
Собирайте число выполненных и отклонённых вызовов, ошибки по типам, время ожидания, переходы состояний, использование fallback и загрузку выделенных ресурсов. Для асинхронного продолжения нужны ещё возраст незавершённых операций и подтверждение результата. Имена и смысл показателей должны оставаться понятными команде.
Особенно внимательно учитывайте быстрые ответы запасного пути. Среднее время страницы может улучшиться после отключения проблемной интеграции, хотя полноценная функция всё ещё не работает. В отчёте различайте нормальный результат, допустимое ограничение и неуспех; одна общая зелёная отметка скроет разницу.
Уведомление должно объяснять затронутую функцию и действие ответственного. Сообщение о каждом отклонённом вызове быстро создаст шум. Обычно полезнее сообщать о переходе состояния, длительном ограничении или нарушении обещанного результата, сохраняя подробности в диагностике.
Как проверить защиту до настоящего сбоя
Используйте контролируемую среду и тестовые данные. Для интеграции подготовьте несколько вариантов: быстрый отказ, медленный ответ, неправильное содержимое успешного ответа и восстановление после паузы. Цель испытания — подтвердить выбранные границы и сохранность операций, а не просто получить исключение в журнале.
- Ожидание действительно завершается в предусмотренных пределах, включая очередь перед вызовом.
- После срабатывания защита ограничивает новые обращения именно к нужной операции.
- Неисправная зависимость не занимает все ресурсы критичного пользовательского пути.
- Fallback сохраняет согласованные данные и показывает понятное ограничение.
- Неопределённый результат записи не приводит к неконтролируемому повторению действия.
- Пробные обращения ограничены, а возврат функции не стирает пользовательский ввод.
- Команда получает сигнал и может отличить восстановление зависимости от работы запасного пути.
Проверьте также поведение при перезапуске экземпляра: сохраняется ли ограничение или счётчики начинаются заново. Оба варианта требуют понимания последствий. Если все экземпляры одновременно сбрасывают состояние, они могут вновь направить поток к ещё неисправному сервису.
Зафиксируйте версию настроек, сценарий, ожидаемый и фактический результат. После смены клиентской библиотеки или политики повторов повторите затронутые проверки. Порядок сочетания ограничителей может изменить и нагрузку, и смысл учитываемых ошибок.
С чего начать владельцу сайта
Выберите одну интеграцию, отказ которой уже мешал заявкам или способен занять общие ресурсы. Попросите разработчика описать вызов, ожидание, повторы, состояние данных и поведение посетителя при отказе. На этой основе проще согласовать работу и проверить результат, чем ставить общую задачу «сделать сайт отказоустойчивым».
В карточке решения укажите владельца функции, границу ограничения, классификацию ошибок, параметры ожидания, запасное поведение, критерий восстановления и дату испытания. Любое временное ручное отключение должно иметь причину, ответственного и условие пересмотра. Иначе защитный режим может остаться включённым после устранения проблемы.
Для внешнего контроля HTTP-доступности важных страниц используйте SiteGuard. Сопоставляйте результаты с внутренними сигналами circuit breaker и проверкой запасного пути, которую выполняет команда.