Laravel Fuse: Circuit Breaker package для queue jobs

0
Перекладено ШІ
Оригінал: Laravel News
Оновлено: 01 лютого, 2026
Втомилися від черг у Laravel, які зависають через падіння зовнішніх сервісів? Laravel Fuse реалізує circuit breaker: миттєво відкидає проблемні запити, повертає їх у чергу й поновлює обробку, коли сервіс знову працює.

Я щойно презентував Laravel Fuse на сцені Laracon India 2026. Це пакет, який я створив, щоб вирішити знайому проблему: робочі черги зупиняються, коли падає зовнішній сервіс.

Проблема

Уявіть: 23:00, у Stripe — збій. Ваші воркери про це не знають. Вони продовжують намагатися списувати гроші, і кожна задача чекає 30 секунд на таймаут, перш ніж впасти — а потім повторити спробу. І знову чекати.

Якщо в черзі 10 000 платіжних задач і кожна чекає по 30 секунд — на очищення черги піде понад 25 годин, хоча всі запити гарантовано падають.

Fuse вирішує це через реалізацію патерну circuit breaker. Після заданої кількості невдач він припиняє робити запити. Задачі відпадають за мілісекунди замість очікування таймаутів і автоматично повертаються в чергу з відстрочкою для повторної обробки. Коли сервіс відновлюється, Fuse це виявляє і відновлює роботу.

Як працює Circuit Breaker

Circuit breaker має три стани:

CLOSED — звичайна робота. Всі запити йдуть до зовнішнього сервісу. У фоні Fuse відстежує успіхи й помилки у хвилинних бакетах, які автоматично звільняються.

OPEN — режим захисту. Якщо відсоток помилок перевищує поріг, ланцюг розмикається. Задачі одразу падають без виклику API і без 30‑секундних таймаутів; вони повертаються в чергу з затримкою. Черга продовжує рух.

HALF-OPEN — тест відновлення. Після таймауту (налаштовується для кожного сервісу) Fuse пропускає одну пробну заявку. Якщо вона успішна — ланцюг закривається; якщо ні — відбувається повторне відкриття і очікування.

Інсталяція

composer require harris21/laravel-fuse

Опублікуйте конфіг:

php artisan vendor:publish --tag=fuse-config

Базове використання

Додайте middleware до задачі, яка викликає зовнішній сервіс:

use Harris21\Fuse\Middleware\CircuitBreakerMiddleware;
 
class ChargeCustomer implements ShouldQueue
{
    public $tries = 0;
    public $maxExceptions = 3;
 
    public function middleware(): array
    {
        return [new CircuitBreakerMiddleware('stripe')];
    }
 
    public function handle(): void
    {
        Stripe::charges()->create([
            'amount' => $this->amount,
            'currency' => 'usd',
            'customer' => $this->customerId,
        ]);
    }
}

Налаштування $tries = 0 дозволяє необмежену кількість release (у термінах Laravel released jobs не рахуються як “retries”), а $maxExceptions = 3 обмежує фактичні помилки. Саму задачу змінювати не потрібно — Fuse обгортає її.

Конфігурація

Опублікований конфіг дозволяє встановити значення за замовчуванням і для кожного сервісу окремо:

// config/fuse.php
 
return [
    'enabled' => env('FUSE_ENABLED', true),
 
    'default_threshold' => 50,
    'default_timeout' => 60,
    'default_min_requests' => 10,
 
    'services' => [
        'stripe' => [
            'threshold' => 50,
            'timeout' => 30,
            'min_requests' => 5,
        ],
        'mailgun' => [
            'threshold' => 60,
            'timeout' => 120,
            'min_requests' => 10,
        ],
    ],
];

threshold — відсоток помилок, при якому ланцюг відкривається. timeout — секундах до наступної перевірки відновлення. min_requests — мінімальна кількість запитів, щоб уникнути тригеру на малих вибірках.

Інтелектуальна класифікація помилок

Не кожна помилка означає, що сервіс впав. Наприклад, 429 — це rate limit, сервіс працює, просто занадто багато запитів. Те ж стосується помилок аутентифікації.

Fuse рахує як невдачі лише ті випадки, що вказують на проблему сервісу:

Це зменшує хибні спрацювання. Ланцюг не відкриється просто через неактивний або неправильний API‑ключ.

Підтримка пікових годин

У робочі години ви можете бути толерантніші до помилок, щоб зберегти пропускну здатність, а поза ними — швидше захиститись. Fuse підтримує це:

'stripe' => [
    'threshold' => 40,
    'peak_hours_threshold' => 60,
    'peak_hours_start' => 9,
    'peak_hours_end' => 17,
],

Між 9:00 і 17:00 використовується поріг 60%, поза цими годинами — 40%. Так ви коригуєте баланс між захистом і throughput залежно від важливості транзакцій.

Події

Fuse відсилає Laravel‑події при кожній зміні стану:

use Harris21\Fuse\Events\CircuitBreakerOpened;
use Harris21\Fuse\Events\CircuitBreakerHalfOpen;
use Harris21\Fuse\Events\CircuitBreakerClosed;

Їх можна слухати для алертів:

class AlertOnCircuitOpen
{
    public function handle(CircuitBreakerOpened $event): void
    {
        Log::critical("Circuit opened for {$event->service}", [
            'failure_rate' => $event->failureRate,
            'attempts' => $event->attempts,
            'failures' => $event->failures,
        ]);
 
        // Send to Slack, page on-call, etc.
    }
}

Подія CircuitBreakerOpened містить ім’я сервісу, поточний відсоток помилок, число спроб і кількість невдач — достатньо для дебагу та алертингу.

Пряме використання

Circuit breaker можна використовувати і поза чергами:

use Harris21\Fuse\CircuitBreaker;
 
$breaker = new CircuitBreaker('stripe');
 
if (!$breaker->isOpen()) {
    try {
        $result = Stripe::charges()->create([...]);
        $breaker->recordSuccess();
        return $result;
    } catch (Exception $e) {
        $breaker->recordFailure($e);
        throw $e;
    }
} else {
    return $this->fallbackResponse();
}

Можна також перевіряти стан і скинути вручну:

$breaker->isClosed();
$breaker->isOpen();
$breaker->isHalfOpen();
$breaker->getStats();
$breaker->reset();

Запобігання thundering herd

Коли ланцюг переходить у HALF-OPEN, небажано, щоб 50 воркерів одночасно робили пробні запити. Fuse використовує Cache::lock(), щоб лише один воркер тестував сервіс; інші падають швидко, поки проба не завершиться.

Вимоги

Пакет не має зовнішніх залежностей — він використовує вбудований cache і механізм lock у Laravel.

Посилання

Патерн circuit breaker походить із книжки Michael Nygard "Release It!" і став відомим завдяки Martin Fowler. Fuse приніс його в Laravel з нативною інтеграцією та мінімальною конфігурацією для початку роботи.

Популярні

Logomark Logotype

Що нового в PHP 8.5

PHP 8.5 обіцяє безліч нових можливостей, таких як оператор Pipe, функції `array_first()` та `array_last()`, а також нове розширення URI. Чи готові ви дізнатися, як ці функції можуть спростити вашу розробку? Читайте далі, щоб дізнатися більше про ці захоплюючі нововведення

Logomark Logotype

Використання повнотекстового пошуку в Laravel

Laravel пропонує потужні можливості повнотекстового пошуку за допомогою методів whereFullText та orWhereFullText, що дозволяють здійснювати складні запити до бази даних. Дізнайтеся, як реалізувати ефективний пошук для вашого блогу чи системи управління контентом

Logomark Logotype

Простий пакет RabbitMQ для Laravel

Вам цікаво дізнатися, як спростити інтеграцію RabbitMQ у вашому Laravel-додатку? У нашій статті ми розглянемо пакет Simple RabbitMQ, який дозволяє легко налаштувати багатозʼєднання, публікувати повідомлення та обробляти черги за допомогою простого синтаксису. Читайте далі, щоб дізнатися більше!