harris21/laravel-fuse 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

harris21/laravel-fuse

Composer 安装命令:

composer require harris21/laravel-fuse

包简介

Circuit breaker for Laravel queue jobs. Protect your workers from cascading failures.

README 文档

README

Fuse for Laravel

Circuit breaker for Laravel queue jobs

Protect your queue workers from cascading failures when external services go down.

The Problem

When Stripe goes down at 11 PM, your queue workers don't know. They keep trying to charge customers. Each job waits 30 seconds for a timeout. Then retries. Waits again. Your entire queue system freezes.

Without Fuse: 10,000 jobs × 30-second timeouts = 25+ hours to clear the queue.

With Fuse: Circuit opens after 5 failures. Queue clears in 10 seconds. Automatic recovery when the service returns.

Features

  • Three-State Circuit Breaker — CLOSED (normal), OPEN (protected), HALF-OPEN (testing recovery)
  • Intelligent Failure Classification — 429 rate limits and auth errors don't trip the circuit
  • Peak Hours Support — Different thresholds for business hours vs. off-peak
  • Fixed Window Tracking — Minute-based buckets with automatic expiration, no cleanup needed
  • Thundering Herd PreventionCache::lock() ensures only one worker probes during recovery
  • Zero Data Loss — Jobs are delayed with release(), not failed permanently
  • Automatic Recovery — Circuit tests and heals itself when services return
  • Per-Service Circuits — Separate breakers for Stripe, Mailgun, your microservices
  • Laravel Events — Get notified on state transitions for alerting and monitoring
  • Real-Time Status Page — Built-in monitoring dashboard with live state updates
  • Pure Laravel — No external dependencies, uses Cache and native job middleware

How It Works

Circuit Breaker States

CLOSED — Normal operations. All requests pass through. Failures are tracked in the background.

OPEN — Protection mode. After the failure threshold is exceeded, the circuit trips. Jobs fail instantly (1ms, not 30s) and are delayed for automatic retry. No API calls are made.

HALF-OPEN — Testing recovery. After the timeout period, one probe request tests if the service recovered. Success closes the circuit. Failure reopens it.

Installation

composer require harris21/laravel-fuse

Publish the configuration:

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

Quick Start

Add the middleware to your job:

use Harris21\Fuse\Middleware\CircuitBreakerMiddleware;

class ChargeCustomer implements ShouldQueue
{
    public $tries = 0;           // Unlimited releases
    public $maxExceptions = 3;   // Only real failures count

    public function middleware(): array
    {
        return [new CircuitBreakerMiddleware('stripe')];
    }

    public function handle(): void
    {
        // Your payment logic - unchanged
        Stripe::charges()->create([...]);
    }
}

That's it. Your job is now protected.

Configuration

// config/fuse.php

return [
    'enabled' => env('FUSE_ENABLED', true),

    'default_threshold' => 50,      // Failure rate percentage to trip circuit
    'default_timeout' => 60,        // Seconds before testing recovery
    'default_min_requests' => 10,   // Minimum requests before evaluating

    'services' => [
        'stripe' => [
            'threshold' => 50,
            'timeout' => 30,
            'min_requests' => 5,

            // Peak hours: more tolerant during business hours
            'peak_hours_threshold' => 60,
            'peak_hours_start' => 9,   // 9 AM
            'peak_hours_end' => 17,    // 5 PM
        ],
        'mailgun' => [
            'threshold' => 60,
            'timeout' => 120,
            'min_requests' => 10,
        ],
    ],

    // Cache prefix — change if multiple apps share the same Redis instance
    'cache' => [
        'prefix' => env('FUSE_CACHE_PREFIX', 'fuse'),
    ],
];

Peak Hours

Configure different thresholds for business hours when every transaction matters:

'stripe' => [
    'threshold' => 40,              // Off-peak: more sensitive (40%)
    'peak_hours_threshold' => 60,   // Peak hours: more tolerant (60%)
    'peak_hours_start' => 9,        // 9 AM
    'peak_hours_end' => 17,         // 5 PM
],

During peak hours (9 AM - 5 PM), the circuit uses the higher threshold to maximize successful transactions. Outside peak hours, it uses the lower threshold for earlier protection.

Intelligent Failure Classification

Not all errors indicate a service is down. Fuse only counts real outages:

Error Type Counted as Failure? Reason
500, 502, 503 Yes Server errors indicate service problems
Connection timeout Yes Service is unreachable
Connection refused Yes Service is unreachable
429 Too Many Requests No Service is healthy, just rate limiting
401 Unauthorized No Your API key is wrong, not a service issue
403 Forbidden No Permission issue, not a service outage
400 Bad Request Yes Could indicate API issues
404 Not Found Yes Could indicate API changes

This prevents false positives. A rate limit doesn't mean Stripe is down - it means you're sending too many requests.

Custom Failure Classification

The default behavior works well for most APIs, but some services deviate from HTTP standards. For example, Stripe returns 500 for idempotency errors that are actually client-side issues — not outages.

You can override the failure classification logic per service by setting the failure_classifier option in your service config:

// config/fuse.php

'services' => [
    'stripe' => [
        'threshold' => 50,
        'timeout' => 30,
        'min_requests' => 5,
        'failure_classifier' => \App\Fuse\StripeFailureClassifier::class,
    ],
],

Extending the Default Classifier

The easiest approach is to extend DefaultFailureClassifier and override specific cases:

namespace App\Fuse;

use GuzzleHttp\Exception\ServerException;
use Harris21\Fuse\Classifiers\DefaultFailureClassifier;
use Throwable;

class StripeFailureClassifier extends DefaultFailureClassifier
{
    public function shouldCount(Throwable $e): bool
    {
        // Stripe returns 500 for idempotency errors — not a real outage
        if ($e instanceof ServerException) {
            $body = (string) $e->getResponse()?->getBody();

            if (str_contains($body, 'idempotency')) {
                return false;
            }
        }

        return parent::shouldCount($e);
    }
}

Implementing the Interface from Scratch

For full control, implement FailureClassifier directly:

namespace App\Fuse;

use Harris21\Fuse\Contracts\FailureClassifier;
use Throwable;

class CustomFailureClassifier implements FailureClassifier
{
    public function shouldCount(Throwable $e): bool
    {
        // Your classification logic
    }
}

When no failure_classifier is configured, Fuse uses DefaultFailureClassifier which preserves the behavior described in the table above.

Events

Fuse dispatches Laravel events on every state transition:

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

Listening to Events

// app/Listeners/AlertOnCircuitOpen.php

class AlertOnCircuitOpen
{
    public function handle(CircuitBreakerOpened $event): void
    {
        Log::critical("Circuit breaker opened for {$event->service}", [
            'failure_rate' => $event->failureRate,
            'attempts' => $event->attempts,
            'failures' => $event->failures,
        ]);

        // Send Slack notification, page on-call, etc.
    }
}

Event Properties

CircuitBreakerOpened:

  • $service — The service name (e.g., "stripe")
  • $failureRate — Current failure percentage
  • $attempts — Total requests in the window
  • $failures — Failed requests in the window

CircuitBreakerHalfOpen:

  • $service — The service name

CircuitBreakerClosed:

  • $service — The service name

Status Page

Fuse includes a real-time monitoring dashboard that shows the state of all your circuit breakers.

Fuse Status Page

Enable the Status Page

Add to your .env:

FUSE_STATUS_PAGE_ENABLED=true

The status page is available at /fuse (configurable via FUSE_STATUS_PAGE_PREFIX).

Authorization

Access is controlled by a viewFuse gate. By default, only the local environment is allowed. Override it in your AppServiceProvider:

use Illuminate\Support\Facades\Gate;

Gate::define('viewFuse', function ($user = null) {
    return $user?->isAdmin();
});

Configuration

// config/fuse.php

'status_page' => [
    'enabled' => env('FUSE_STATUS_PAGE_ENABLED', false),
    'prefix' => env('FUSE_STATUS_PAGE_PREFIX', 'fuse'),
    'middleware' => [],          // Custom middleware (replaces default)
    'polling_interval' => 2,    // Frontend refresh interval in seconds
],

What It Shows

  • Circuit state for each configured service (CLOSED, OPEN, HALF-OPEN)
  • State history with timestamped transitions
  • Live stats — attempts, failures, failure rate per window
  • Recovery info — when the circuit opened and when it will test recovery
  • Auto-refresh — polls the backend every 2 seconds (configurable)

Fallback Strategies

When the circuit opens, your application needs a plan. Here are common strategies:

Return cached data — Show last known prices, cached shipping rates, or stale product info. Slightly stale data beats an error page.

Use a fallback service — Switch to a backup payment provider, or show "payment pending" and queue it for later.

Queue for later — Fuse already does this with release(). For synchronous requests, dispatch a job to retry when the circuit closes.

Graceful degradation — Hide the feature entirely. Can't load recommendations? Don't show that section. The page still works.

Direct Usage

Use the circuit breaker directly outside of jobs:

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 {
    // Circuit is open - use fallback
    return $this->fallbackResponse();
}

Check Circuit State

$breaker = new CircuitBreaker('stripe');

$breaker->isClosed();    // Normal operations
$breaker->isOpen();      // Protected, failing fast
$breaker->isHalfOpen();  // Testing recovery

$breaker->getStats();    // Get full statistics
$breaker->reset();       // Manually reset to closed

Requirements

  • PHP 8.3+
  • Laravel 11+
  • Redis recommended for production, file cache may have race conditions during recovery probing

Credits

Built by Harris Raftopoulos for Laracon India 2026.

YouTube: @harrisrafto

Based on the circuit breaker pattern from Michael Nygard's Release It! and popularized by Martin Fowler.

License

MIT

harris21/laravel-fuse 适用场景与选型建议

harris21/laravel-fuse 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 56.41k 次下载、GitHub Stars 达 247, 最近一次更新时间为 2026 年 02 月 03 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「queue」 「laravel」 「jobs」 「fuse」 「circuit-breaker」 「resilience」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 harris21/laravel-fuse 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 harris21/laravel-fuse 我们能提供哪些服务?
定制开发 / 二次开发

基于 harris21/laravel-fuse 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 56.41k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 247
  • 点击次数: 15
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 247
  • Watchers: 1
  • Forks: 8
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-02-03