Современные веб-приложения редко работают в изоляции. Почти всегда они зависят от сторонних API: платежных систем, почтовых сервисов, аналитики и других интеграций. Проблема в том, что если внешний сервис начинает тормозить или полностью падает, это может потянуть за собой и ваше приложение. Решить эту проблему помогает паттерн Circuit Breaker.
Разберёмся, как он работает и как его можно использовать в Laravel.
Что такое Circuit Breaker и зачем он нужен
Circuit Breaker (предохранитель) - это шаблон проектирования, который отслеживает ошибки при обращении к внешнему сервису и временно блокирует повторные попытки, если тот ведёт себя нестабильно.
Без него приложение продолжает слать запросы в неработающий сервис при этом тратит ресурс, увеличивается время ответа и ухудшается пользовательский опыт.
С Circuit Breaker логика меняется:
если ошибок слишком много, запросы временно прекращаются
система "даёт сервису отдохнуть"
через некоторое время пробует снова
Это снижает нагрузку и защищает ваше приложение от цепных сбоев.
Как это работает
У Circuit Breaker есть три состояния:
1. Closed (закрыт)
Нормальный режим. Все запросы проходят. Если количество ошибок превышает порог, состояние меняется.
2. Open (открыт)
Запросы к внешнему сервису блокируются сразу, без попытки выполнения. Вместо этого можно:
вернуть fallback-ответ
показать сообщение об ошибке
использовать кэш
3. Half-Open (полуоткрыт)
Система пробует отправить один или несколько тестовых запросов. Если всё работает то возвращается в Closed, если нет то снова Open.
Реализация в Laravel
В Laravel есть готовые решения, которые упрощают внедрение Circuit Breaker. Обычно они используют кэш (Redis, Memcached) для хранения состояния.
Пример простой реализации через сервис:
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;
class ApiService
{
public function getData()
{
if ($this->isOpen()) {
return $this->fallback();
}
try {
$response = Http::get('https://api.example.com/data');
$this->resetFailures();
return $response->json();
} catch (\Exception $e) {
$this->recordFailure();
return $this->fallback();
}
}
protected function isOpen()
{
return Cache::get('circuit_open', false);
}
protected function recordFailure()
{
$failures = Cache::increment('failures');
if ($failures >= 5) {
Cache::put('circuit_open', true, now()->addSeconds(30));
}
}
protected function resetFailures()
{
Cache::forget('failures');
Cache::forget('circuit_open');
}
protected function fallback()
{
return ['data' => [], 'message' => 'Service unavailable'];
}
}Пример с задержкой и таймаутом
Важно не только отслеживать ошибки, но и ограничивать время ожидания ответа:
$response = Http::timeout(2)->get('https://api.example.com/data');Если сервис отвечает слишком долго, запрос будет прерван, и это тоже можно считать ошибкой для Circuit Breaker.
Использование с повторными попытками
Иногда полезно комбинировать Circuit Breaker с retry-логикой:
$response = Http::retry(3, 100)
->timeout(2)
->get('https://api.example.com/data');Здесь:
3 попытки
задержка 100 мс между ними
Если все попытки неудачны то фиксируем ошибку.
Пример с кастомным fallback
Fallback - это не просто заглушка. Он может быть умнее:
protected function fallback()
{
return Cache::get('cached_api_data', [
'data' => [],
'message' => 'Using cached data',
]);
}А при успешном запросе можно обновлять кэш:
$data = $response->json();
Cache::put('cached_api_data', $data, 300);Пример для платежного сервиса
Допустим, у вас интеграция с платежным API:
public function charge(array $payload)
{
if ($this->isOpen()) {
throw new \Exception('Payment service temporarily unavailable');
}
try {
return Http::post('https://payment.api/charge', $payload)->json();
} catch (\Exception $e) {
$this->recordFailure();
throw $e;
}
}В этом случае лучше явно уведомить пользователя, чем зависнуть на долгом запросе.
Использование готовых пакетов
Чтобы не писать всё вручную, можно использовать пакеты с готовой реализацией Circuit Breaker которые обычно предлагают:
гибкую настройку порогов
автоматическое переключение состояний
интеграцию с Laravel Cache
метрики и логирование
Это особенно полезно в крупных проектах.
Когда стоит использовать Circuit Breaker
Этот паттерн нужен не всегда, но он критически важен, если:
у вас много внешних API
есть платёжные интеграции
важна стабильность и SLA
приложение работает под нагрузкой
В простых проектах можно обойтись retry и таймаутами. Но по мере роста системы Circuit Breaker становится практически обязательным.