Масштабирование и надёжность

Обновлено 5 мин чтения

Circuit Breaker: почему иногда системе полезнее отказаться от запроса, чем продолжать пытаться

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

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

Можно поставить таймаут. Тогда каждый запрос когда-нибудь закончится. Но следующий сотрудник снова отправится ждать того же поставщика, и за ним следующий. Circuit Breaker добавляет к отдельному ограничению времени память о недавних сбоях: стоит ли сейчас вообще начинать очередное обращение?

Прекратить вызовы — не значит исправить поставщика

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

Такая защита полезна, если неработающий участник способен ухудшить соседние сценарии. Если вызовы редкие, строго ограничены по времени и уже изолированы, дополнительная машина состояний может не окупить сопровождение. Само наличие внешнего API ещё не повод оборачивать каждую операцию в Circuit Breaker.

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

Три состояния, которые придётся обслуживать

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

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

После паузы разрешаются ограниченные пробные обращения — half-open. Их задача проверить восстановление, не отправляя весь поток сразу. При хорошем результате обычные вызовы возобновляются, при плохом защита остаётся активной. Условия перехода и число проб зависят от реализации и нагрузки.

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

Не каждый отказ означает аварию

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

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

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

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

Что увидит сотрудник в разомкнутом состоянии

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

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

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

Восстановление — отдельная часть испытания

Для проверки недостаточно выключить зависимость и увидеть быстрый отказ. Нужно затем вернуть её с ограниченной производительностью. Не создают ли пробные вызовы и накопленные задания новый всплеск? Может ли механизм застрять в разомкнутом состоянии? Не расходятся ли статистика библиотеки и фактическая доступность функции для сотрудников?

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

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

У конфигурации нужен ответственный: кто меняет пороги, расследует частые переключения и может осторожно вмешаться при ошибочном отключении. Иначе небольшая библиотека становится ещё одной системой принятия решений, которую никто не готов объяснить во время инцидента.