Circuit Breaker Pattern: منع الانهيار المتسلسل

2025-11-27 وقت القراءه : 3 دقائق


ما هو Circuit Breaker؟

نمط تصميم يمنع تطبيقك من محاولة استدعاء خدمة خارجية فاشلة بشكل متكرر. يعمل مثل "الفيوز الكهربائي" - عندما يكتشف مشاكل، يوقف الطلبات مؤقتاً.


حالات Circuit Breaker

  • Closed (مغلق): كل شيء يعمل بشكل طبيعي، الطلبات تمر
  • Open (مفتوح): فشل كثير، توقف الطلبات مؤقتاً
  • Half-Open (نصف مفتوح): يسمح بطلبات تجريبية للتحقق من الشفاء


التطبيق في Laravel

Laravel لا يوفر Circuit Breaker بشكل مباشر، لكن يمكننا تطبيقه:

php
// app/Services/CircuitBreaker.php
namespace App\Services;


use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;


class CircuitBreaker
{
    private string $serviceName;
    private int $failureThreshold;
    private int $timeout;
    
    public function __construct(
        string $serviceName,
        int $failureThreshold = 5,
        int $timeout = 60
    ) {
        $this->serviceName = $serviceName;
        $this->failureThreshold = $failureThreshold;
        $this->timeout = $timeout;
    }
    
    public function call(callable $callback, callable $fallback = null)
    {
        $state = $this->getState();
        
        // إذا كان Circuit مفتوح (open)
        if ($state === 'open') {
            if ($this->shouldAttemptReset()) {
                $this->setState('half-open');
            } else {
                Log::warning("Circuit breaker is open for {$this->serviceName}");
                return $fallback ? $fallback() : $this->getDefaultResponse();
            }
        }
        
        try {
            $result = $callback();
            $this->onSuccess();
            return $result;
        } catch (\Exception $e) {
            $this->onFailure();
            Log::error("Circuit breaker failure for {$this->serviceName}: " . $e->getMessage());
            
            return $fallback ? $fallback() : $this->getDefaultResponse();
        }
    }
    
    private function getState(): string
    {
        return Cache::get("circuit_breaker:{$this->serviceName}:state", 'closed');
    }
    
    private function setState(string $state): void
    {
        Cache::put("circuit_breaker:{$this->serviceName}:state", $state, $this->timeout);
    }
    
    private function getFailureCount(): int
    {
        return (int) Cache::get("circuit_breaker:{$this->serviceName}:failures", 0);
    }
    
    private function incrementFailures(): void
    {
        $count = $this->getFailureCount() + 1;
        Cache::put("circuit_breaker:{$this->serviceName}:failures", $count, 300);
        
        if ($count >= $this->failureThreshold) {
            $this->setState('open');
            Cache::put("circuit_breaker:{$this->serviceName}:opened_at", now(), $this->timeout);
        }
    }
    
    private function resetFailures(): void
    {
        Cache::forget("circuit_breaker:{$this->serviceName}:failures");
        $this->setState('closed');
    }
    
    private function shouldAttemptReset(): bool
    {
        $openedAt = Cache::get("circuit_breaker:{$this->serviceName}:opened_at");
        
        return $openedAt && now()->diffInSeconds($openedAt) >= $this->timeout;
    }
    
    private function onSuccess(): void
    {
        $state = $this->getState();
        
        if ($state === 'half-open') {
            $this->resetFailures();
            Log::info("Circuit breaker closed for {$this->serviceName}");
        }
    }
    
    private function onFailure(): void
    {
        $this->incrementFailures();
    }
    
    private function getDefaultResponse()
    {
        return [
            'success' => false,
            'message' => 'Service temporarily unavailable. Please try again later.',
            'circuit_breaker' => true
        ];
    }
}



استخدام Circuit Breaker

php
// في Service أو Controller
use App\Services\CircuitBreaker;
use Illuminate\Support\Facades\Http;


class PaymentService
{
    private CircuitBreaker $circuitBreaker;
    
    public function __construct()
    {
        $this->circuitBreaker = new CircuitBreaker(
            serviceName: 'payment_gateway',
            failureThreshold: 3,
            timeout: 60
        );
    }
    
    public function processPayment(Order $order)
    {
        return $this->circuitBreaker->call(
            // المحاولة الأساسية
            callback: function () use ($order) {
                $response = Http::timeout(5)
                    ->post('https://payment-api.com/charge', [
                        'amount' => $order->total,
                        'currency' => 'USD',
                        'order_id' => $order->id,
                    ]);
                
                if ($response->failed()) {
                    throw new \Exception('Payment API returned error');
                }
                
                return $response->json();
            },
            // الحل البديل (Fallback)
            fallback: function () use ($order) {
                // حفظ الطلب للمعالجة اللاحقة
                $order->update(['status' => 'payment_pending']);
                
                return [
                    'success' => false,
                    'message' => 'Payment service is temporarily unavailable. Your order has been saved and will be processed shortly.',
                    'order_id' => $order->id,
                ];
            }
        );
    }
}


فوائد Circuit Breaker

✅ يمنع Cascading Failures: فشل خدمة واحدة لا يؤثر على النظام كاملاً

✅ Fast Fail: استجابة سريعة بدلاً من الانتظار الطويل

✅ Self-Healing: النظام يحاول العودة تلقائياً

✅ تجربة مستخدم أفضل: رسائل واضحة بدلاً من Timeout

إضافة تعليق
Loading...