ما هو Circuit Breaker؟
نمط تصميم يمنع تطبيقك من محاولة استدعاء خدمة خارجية فاشلة بشكل متكرر. يعمل مثل "الفيوز الكهربائي" - عندما يكتشف مشاكل، يوقف الطلبات مؤقتاً.
حالات Circuit Breaker
التطبيق في 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