تحديد معدل الطلبات في PHP: حماية API وتحسين التحميل
\nتحديد معدل الطلبات (Rate limiting) هو آلية بالغة الأهمية لأي تطبيق ويب بلغة PHP، خاصة لواجهات API العامة. فهو يمنع إساءة الاستخدام، ويحمي من هجمات DDoS، ويقلل الحمل على الخادم، ويضمن توزيعًا عادلًا للموارد بين المستخدمين. في هذه المقالة، سنستعرض بالتفصيل المكتبات والأدوات الشائعة لتطبيق تحديد معدل الطلبات في PHP.
\n\nالخوارزميات الأساسية لتحديد معدل الطلبات
\nقبل اختيار مكتبة، من المهم فهم الخوارزميات الأساسية:
\n- \n
- Token Bucket (دلو الرموز) — الخوارزمية الأكثر مرونة. يحتوي الدلو على N من الرموز، كل طلب يستهلك رمزًا. يتم استعادة الرموز بمعدل محدد. \n
- Leaky Bucket (الدلو المتسرب) — تتم معالجة الطلبات بمعدل ثابت. يتم تجاهل الطلبات الزائدة أو وضعها في قائمة انتظار. \n
- Fixed Window (النافذة الثابتة) — يتم حساب الطلبات خلال فترة زمنية ثابتة (مثل 60 ثانية). بسيط، لكنه قد يسمح بذروات التحميل عند حدود النوافذ. \n
- Sliding Window (النافذة المنزلقة) — خوارزمية أكثر دقة، تأخذ في الاعتبار الطلبات في نافذة زمنية متحركة. تستخدم السجلات أو العدادات مع الطوابع الزمنية. \n
المكتبات الشائعة لتحديد معدل الطلبات في PHP
\n\n1. Symfony Rate Limiter (symfony/rate-limiter)
\nمكون Symfony الرسمي، لكنه يعمل أيضًا كمكتبة مستقلة. يدعم جميع الخوارزميات الرئيسية: Token Bucket، Fixed Window، Sliding Window. مثالي للمشاريع على Symfony أو Laravel.
\nuse Symfony\\Component\\RateLimiter\\RateLimiterFactory;\nuse Symfony\\Component\\RateLimiter\\Storage\\InMemoryStorage;\n\n$factory = new RateLimiterFactory([\n 'id' => 'api',\n 'policy' => 'token_bucket',\n 'limit' => 100,\n 'rate' => ['interval' => '1 minute'],\n], new InMemoryStorage());\n\n$limiter = $factory->create('user_123');\n\nif ($limiter->consume(1)->isAccepted()) {\n // معالجة الطلب\n} else {\n // إرجاع 429 Too Many Requests\n header('HTTP/1.1 429 Too Many Requests');\n echo json_encode(['error' => 'Rate limit exceeded']);\n}\n\n2. nikic/FastRoute مع middleware لتحديد معدل الطلبات
\nحل خفيف للمشاريع بدون أطر عمل. يستخدم تطبيق PHP لـ Token Bucket مع تخزين في Redis أو Memcached.
\nclass RateLimitMiddleware {\n private $redis;\n private $maxRequests = 100;\n private $window = 60; // ثانية\n\n public function handle($request, $next) {\n $key = 'rate_limit:' . $_SERVER['REMOTE_ADDR'];\n $current = $this->redis->incr($key);\n \n if ($current === 1) {\n $this->redis->expire($key, $this->window);\n }\n\n if ($current > $this->maxRequests) {\n return new Response('Too Many Requests', 429);\n }\n\n return $next($request);\n }\n}\n\n3. Laravel Rate Limiter (مدمج)
\nفي Laravel، تحديد معدل الطلبات مدمج في النواة. يستخدم Redis أو ذاكرة التخزين المؤقت. يدعم المحددات المسماة والتقسيم حسب المستخدمين.
\n// في RouteServiceProvider\nuse Illuminate\\Cache\\RateLimiting\\Limit;\nuse Illuminate\\Http\\Request;\n\nprotected function configureRateLimiting()\n{\n RateLimiter::for('api', function (Request $request) {\n return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());\n });\n}\n\n// في المتحكم\npublic function index()\n{\n $executed = RateLimiter::attempt(\n 'send-message:'.$user->id,\n $perMinute = 5,\n function() {\n // إرسال الرسالة\n }\n );\n \n if (! $executed) {\n return response('Too Many Attempts.', 429);\n }\n}\n\n4. PHP RateLimit (php-rateLimit/php-rateLimit)
\nمكتبة عالمية