تقييد الطلب كيفية التوقف عن تلقي الرسائل 429 من كل واجهة برمجة تطبيقات تستخدمها

نُشرت في

HTTP 429 عدد الطلبات الزائد — كيفية قراءة عناصر إعادة المحاولة، وتطبيق التراجع المُناسب مع التحريك، وفهم خوارزميات حاوية التوقيت مقابل حاوية التسرب، وتطبيق تقييد السرعة على واجهة برمجة تطبيقاتك.

تقييد الطلب: كيف توقف عن تلقي الرسائل 429 من كل واجهة برمجة تطبيقات تستخدمها
إعلان · حذف؟

أنت تصل إلى 429. قدّم سكربتك للواجهة البرمجية خلال الساعات الماضية، وسجلاتك مليئة بالأحمر، والنشر يقترب من 20 دقيقة. هذه اللحظة هي اللحظة التي تصبح فيها هذه المشكلة غير مجردة.

الحالة HTTP 429 "عدد الطلبات الزائدة" تعني أنك أرسلت أكثر من عدد الطلبات المسموح به في فترة زمنية معينة. فهم الرد بشكل صحيح — ومحاولة التكرار بشكل صحيح — هو مهارة يتعلمها معظم المطورين بعد أن يُحرّفون.

ما الذي يخبرك به 429 فعليًا

الرقم المرجعي هو فقط نصف الرسالة. المعلومات الحقيقية موجودة في الرسائل:

  • Retry-After: 30 — انتظر 30 ثانية قبل إعادة المحاولة. يمكن أن يكون أيضًا تاريخ HTTP: Retry-After: Mon, 08 Jun 2026 15:00:00 GMT
  • X-RateLimit-Limit: 100 — عدد الطلبات المسموح بها في النافذة
  • X-RateLimit-Remaining: 0 — عدد الطلبات المتبقية في النافذة الحالية (أنت عند الصفر)
  • X-RateLimit-Reset: 1749391200 — توقيت Unix عند إعادة تعيين النافذة

لا ترسل كل الأنظمة هذه. ترسل GitHub كل الأنظمة. ترسل Stripe Retry-After. بعض واجهات برمجة REST لا ترسل أي شيء وتتوقع أن تُفترض. Retry-After، استخدمها بالضبط — فهي تخبرك بالحد الأدنى للانتظار الآمن. إذا لم تفعل ذلك، فإن التراجع التبادلي هو خيارك الافتراضي.

طريقة الخطا في إعادة المحاولة

التنفيذ الابسط يبدو كالتالي:

async function fetchWithoutBackoff(url) {
  while (true) {
    const res = await fetch(url);
    if (res.ok) return res;
    if (res.status === 429) continue; // immediately retry
  }
}

هذا يضر بشكل فعّال. إذا اصطدمت 10 نسخ من خدمةك في نفس اللحظة وجميعها تعيد المحاولة فورًا، فإن كل محاولة تقع في نفس الوقت — مشكلة "الجيوش المتساوية". تُقيّد مرة أخرى فورًا، في حلقة مغلقة يمكن أن تستمر إلى الأبد، مما يجعل عميلك يبدو وكأنه يُستخدم الواجهة بشكل مقصود.

العودة التبادلية مع التحديد العشوائي

النمط الصحيح: كل محاولة تنتظر أكثر من المرة السابقة (مُضاعفة)، و-offset عشوائي يمنع إعادة المحاولة المتماسكة بين عدة عميلين (التحديد).

async function fetchWithBackoff(url, options = {}, maxRetries = 5) {
  let attempt = 0;

  while (attempt <= maxRetries) {
    const res = await fetch(url, options);

    if (res.ok) return res;

    if (res.status !== 429) {
      throw new Error(`Request failed: ${res.status}`);
    }

    if (attempt === maxRetries) {
      throw new Error(`Rate limited after ${maxRetries} retries`);
    }

    // Use Retry-After if provided; otherwise exponential backoff + jitter
    const retryAfter = res.headers.get('Retry-After');
    let waitMs;

    if (retryAfter) {
      const seconds = isNaN(retryAfter)
        ? (new Date(retryAfter) - Date.now()) / 1000  // HTTP date
        : Number(retryAfter);                          // seconds
      waitMs = seconds * 1000;
    } else {
      const baseDelay = 1000 * Math.pow(2, attempt); // 1s, 2s, 4s, 8s, 16s
      const jitter = Math.random() * 1000;            // 0–1000ms random offset
      waitMs = baseDelay + jitter;
    }

    console.log(`Rate limited. Waiting ${Math.round(waitMs / 1000)}s (attempt ${attempt + 1}/${maxRetries})`);
    await new Promise(resolve => setTimeout(resolve, waitMs));
    attempt++;
  }
}

الخط المخصص هو الجزء الذي يُغفله معظم التنفيذ. بدونه، تصل إعادة المحاولات من عمليات متوازية إلى مجموعات متماسكة. معه، تنتشر على مدى الانتظار.

لواجهات برمجة تُرجع Retry-After، استخدم هذا القيمة كـ الحد الأدنى — إذا استمرت في الحصول على 429 بعد الانتظار المحدد، فقم بتطبيق التراجع التبادلي على الأعلى.

السلة المُجمعة مقابل السلة المُتسربة

تسيطر على تطبيقات تقييد الطلب نوعان من الخوارزميات. فهم أي نوع تتعامل معه يخبرك كثيرًا عن سلوك الواجهة البرمجية تحت الضغط — ونوع الخوارزمية التي يجب أن تختارها عند بناء واجهة برمجة جديدة.

السلة المُجمعة

تُحتفظ السلة بحد أقصى من N مُسموح به. كل طلب يُستهلك 1 مسموح به. تُملأ المسموحات بسرعة ثابتة (مثلاً 10 في الثانية). إذا كانت السلة فارغة، يتم رفض الطلب أو تأجيله.

مُناسب للإطلاق المفاجئ. إذا لم تُرسل طلبات في فترة طويلة، فإنك تُجمع مسموحات، ويمكنك إرسال موجة دون أن تتجاوز الحد. تعمل واجهة برمجة غوغل بهذا الشكل — 5000 طلب في الساعة، لكنك يمكن أن تستخدمها في وقت واحد إذا لم تستخدمها في الساعات السابقة. مناسب للحالات التفاعلية حيث تكون الطلب متفاوتة.

السلة المُتسربة

تُدخل الطلبات إلى قائمة انتظار وتُخرج بسرعة ثابتة، بغض النظر عن سرعة وصولها. إذا تمت ملء القائمة، يتم إسقاط الطلبات الجديدة.

مخرج متسق، لا توجد موجات. حتى لو كان لديك مساحة مسموح بها، تُخرج الطلبات ببطء محدد. يُستخدم هذا في مودول Nginx. limit_req مفيد لحماية الأنظمة المُستقبلية من الزيادة المفاجئة — مفيد لتسليم الإشعارات، المكالمات الخارجة، وأي شيء حيث يُهم التدفق المتسق أكثر من التحمل المفاجئ.

ما الذي يجب اختياره عند بناء واجهة برمجة جديدة: الواجهات الموجهة للعميل التي تحتاج إلى تحميل مفاجئ → السلة المُجمعة. توصيل الإشعارات الخارجة أو مكالمات واجهات برمجة خارجية → السلة المُتسربة. المهام الخلفية التي تُهم التدفق المتسق → السلة المُتسربة.

حساب معدل الطلبات الآمنة

قبل كتابة أي منطق إعادة المحاولة، اكتشف ما الذي يسمح به حقًا. إذا قالت واجهة برمجة "1000 طلب في الساعة"، فإن ذلك يعادل 16.67 طلب في الدقيقة أو 0.278 طلب في الثانية. أضف مساحة أمان 20% وستكون عند حوالي 13 طلبًا في الدقيقة — كافية لتجنب مشكلات التوقيت المحدودة حيث تتقاطع النافذتان.

استخدم مُحسّن لحساب معدل الطلب لتحويل أرقام الحد إلى معدلات في الثانية والدقيقة، وتحديد الفاصل الزمني المناسب بين الطلبات، وفهم كيف يؤثر مستوى التوازي على خطر الموجات.

تطبيق تقييد الطلب على واجهة برمجة جديدة

إذا كنت على الجانب الآخر وترغب في إضافة سلوك 429 الصحيح لواجهتك:

  1. اختر التفاصيل المناسبة. التفصيل حسب العنوان IP سهل لكنه ينهار عند الخدمات التي تستخدم مُحول IP مشترك. التفاصيل حسب مفتاح الواجهة أفضل لكنها تتطلب التحقق. التفاصيل حسب معرف المستخدم هو الأفضل عندما يكون لديك معرف. لا تُدمج التفاصيل دون معرفة أي منها يفوز.
  2. أعد إرسال Retry-After. بشكل 429 بدون Retry-After يُجبر كل عميل على تطوير خوارزمية تراجع. ستحصل على أكثر من "الجيوش المتساوية"، وليس أقل.
  3. استخدم Redis لتقييد الطلب بشكل موزع. العددين في الذاكرة لا يعملون بين عدة مثيلات. Redis هو النمط القياسي. تُستخدم مكتبات مثل INCR + EXPIRE rate-limiter-flexible (نودي) و slowapi (بايثون/فاستآيبي) تُبسّط هذا بشكل صحيح. سجل كل 429 تُرسله.
  4. زيادة في 429 من مفتاح واحد هي إما خطأ في العميل أو انتهاك مقصود. كلاهما يستحق معرفته في الوقت الحقيقي. لا تقيّد الطلب عند فشل التحقق.
  5. أعد إرسال 401 لبيانات غير صحيحة، وليس 429. تقييد الطلب عند فشل التحقق هو طريقة تُغلق عميلك أثناء تغيير المعرف. ما الذي يجب أن تفعله الآن

إذا كنت تواجه 429:

أولًا — استخدمه إذا كان موجودًا، لا اخترع تأخيرًا خاصًا

  • أكمل Retry-After أعد تطبيق التراجع التبادلي مع التحديد العشوائي — الكود أعلاه جاهز للنسخ
  • سجل الرأسية في كل رد — قد تُستهلك الحدود أسرع مما تظن
  • احتفظ بالردود عند عدم تغيير البيانات بانتظام X-RateLimit-Remaining إذا كنت تُطبق تقييد الطلب: اختر مكتبة مبنية على Redis، أعد إرسال
  • على كل 429، مراقبة معدل 429 لكل مفتاح، ولا تقيّد الطلب عند فشل التحقق.

الحالة 429 ليست العدو — هي الواجهة البرمجية التي تخبرك بالضبط ما حدث و(غالبًا) كم يجب الانتظار. معظم مشكلات تقييد الطلب تأتي من تجاهل هذه الرسالة ومحاولة التكرار فورًا. لا تفعل ذلك. Retry-After تقييد الطلب: كيف توقف عن تلقي الرسائل 429 من كل واجهة برمجة تطبيقات تستخدمها 2

تقييد الطلب: كيف توقف عن تلقي الرسائل 429 من كل واجهة برمجة تطبيقات تستخدمها 1

هل تريد حذف الإعلانات؟ تخلص من الإعلانات اليوم

تثبيت ملحقاتنا

أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع

أضف لـ إضافة كروم أضف لـ امتداد الحافة أضف لـ إضافة فايرفوكس أضف لـ ملحق الأوبرا

وصلت لوحة النتائج!

لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!

إعلان · حذف؟
إعلان · حذف؟
إعلان · حذف؟

ركن الأخبار مع أبرز التقنيات

شارك

ساعدنا على الاستمرار في تقديم أدوات مجانية قيمة

اشتري لي قهوة
إعلان · حذف؟