استراتيجية إعادة محاولة الـ API والتأخير الأسي: أفضل الأنماط المجربة

أتقِن التراجع الأُسي مع التذبذب العشوائي الكامل، ورؤوس Retry-After، ومفاتيح الثباتية، وقواطع الدوائر، ثم اختبر منطق إعادة المحاولة لواجهة برمجة التطبيقات (API) الخاصة بك باستخدام محاكيات Apidog.

INEZA Felin-Michel

INEZA Felin-Michel

31 أغسطس 2026

استراتيجية إعادة محاولة الـ API والتأخير الأسي: أفضل الأنماط المجربة

Apidog للمؤسسات

النشر على الخوادم المحلية

SSO و RBAC

متوافق مع SOC 2

استكشف Apidog للمؤسسات

فشل استدعاء واجهة برمجة تطبيقات الدفع (API) الخاصة بك في الساعة الثانية صباحًا. هل كان ذلك خللاً في الشبكة، أم تجاوزًا للحد الأقصى للمعدل، أم خادمًا معطلاً؟ الإجابة تحدد ما إذا كانت إعادة المحاولة ستنقذ المعاملة أم ستخصم من العميل مرتين.

تُعد عمليات إعادة المحاولة النمط الأكثر شيوعًا للمرونة في الأنظمة الموزعة، والأكثر إخفاقًا في التنفيذ. حلقة برمجة حول استدعاء HTTP تبدو وكأنها برمجة دفاعية. ولكن عند تطبيقها بشكل خاطئ، فإنها تحوّل انقطاعًا لمدة 30 ثانية إلى انقطاع لمدة 30 دقيقة، لأن آلاف العملاء يضغطون على خادم يعاني في نفس اللحظة. عند تطبيقها بشكل صحيح، تمتص عمليات إعادة المحاولة الإخفاقات العابرة بسلاسة لا يلاحظها المستخدمون أبدًا.

يغطي هذا الدليل منطق إعادة المحاولة الذي تعتمد عليه أنظمة الإنتاج: رموز الحالة التي يجب إعادة محاولتها، وصيغة التراجع الأسي مع التذبذب الكامل، ورؤوس Retry-After، ومفاتيح الثباتية، وميزانيات إعادة المحاولة، وقواطع الدائرة. سترى أيضًا كيفية إثبات أن عميلك يتصرف بشكل صحيح من خلال محاكاة 429 و 503 باستخدام خوادم Apidog الوهمية، لأن نمط إعادة المحاولة الذي لم تختبره أبدًا مقابل خادم فاشل هو تخمين، وليس تصميمًا. تتعلم الفرق التي تبني منطق إعادة محاولة واجهة برمجة تطبيقات التكنولوجيا المالية هذا بالطريقة المكلفة؛ لست مضطرًا لذلك.

لماذا تزيد عمليات إعادة المحاولة الساذجة من سوء الانقطاعات

تخيل خدمة تتعامل مع 1,000 طلب في الثانية. تتعثر لمدة خمس ثوانٍ. يعيد كل عميل المحاولة فورًا، ثلاث مرات لكل منهم. يتحول طلبك البالغ 1,000 طلب في الثانية إلى 4,000 طلب في الثانية موجهة نحو خادم يعاني بالفعل. ينهار تمامًا. الآن يعيد كل عميل المحاولة مرة أخرى.

تُسمى حلقة التغذية الراجعة هذه: عاصفة إعادة المحاولة. والاندفاع المتزامن عندما يعود الخادم إلى العمل يُعرف بالقطيع الهائج. يشير كتاب SRE من Google إلى هذا النمط في فصله حول معالجة الأعطال المتتالية: عمليات إعادة المحاولة بدون تراجع تزيد الحمل بالضبط عندما يكون النظام في أمس الحاجة إليه، ويمكن أن تبقي الخدمة معطلة لفترة طويلة بعد إصلاح الخلل الأصلي.

يتسبب عيبان تصميميان في معظم عواصف إعادة المحاولة:

الحل ليس "لا تعيد المحاولة أبدًا". الحل هو إعادة المحاولة بشكل انتقائي، مع تأخيرات عشوائية متزايدة، ومع سقف صارم على مقدار الحمل الإضافي الذي تضيفه عمليات إعادة المحاولة الخاصة بك.

أعد محاولة هذه الإخفاقات، ولا تعيد محاولة تلك أبدًا

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

أعد محاولة هذه:

الإشارة المعنى
429 Too Many Requests لقد وصلت إلى حد المعدل. تراجع وعد ببطء أكثر.
502 Bad Gateway قفزة عليا أعادت بيانات غير صالحة. غالبًا ما تكون عابرة.
503 Service Unavailable الخادم مثقل أو يعاد تشغيله.
504 Gateway Timeout كانت تبعية عليا بطيئة جدًا.
فصل الاتصال، أخطاء DNS، مهلة المقبس قد لا يكون الطلب قد وصل أبدًا.

مهلة عبّارة 504 تستحق عناية خاصة: قد يكون المصدر قد عالج طلبك على الرغم من أن العبّارة توقفت عن الانتظار. هذا التمييز مهم بمجرد أن نصل إلى الثباتية.

لا تعيد محاولة هذه أبدًا:

الإشارة المعنى
400 Bad Request حمولة طلبك غير صحيحة. ستكون غير صحيحة في المرة القادمة أيضًا.
401 Unauthorized بيانات الاعتماد الخاصة بك خاطئة أو منتهية الصلاحية. قم بتحديث الرمز المميز، لا تدخل في حلقة.
403 Forbidden أنت تفتقر إلى الإذن. إعادة المحاولة لن تمنحك إياه.
422 Unprocessable Entity فشل التحقق من الصحة. أصلح البيانات، وليس التوقيت.

القاعدة: أعد المحاولة عندما يكون الفشل متعلقًا بحالة الخادم أو الشبكة. افشل بسرعة عندما يكون الفشل متعلقًا بطلبك. يقع 429 في المنتصف؛ فهو قابل لإعادة المحاولة، ولكنه أيضًا إشارة إلى أن معدل طلباتك الإجمالي يحتاج إلى عمل، وهي مشكلة تحديد معدل يجب حلها قبل أي حلقة إعادة محاولة.

صيغة التراجع الأسي، ولماذا يُعدّ "التذبذب" مهمًا

التراجع الأسي يعني أن كل إعادة محاولة تنتظر وقتًا أطول من سابقتها، وتتضاعف بشكل افتراضي:

delay = base * 2^retry_count

مع قاعدة 500 مللي ثانية، يكون التأخير 0.5 ثانية، 1 ثانية، 2 ثانية، 4 ثوانٍ، 8 ثوانٍ. أضف حدًا أقصى (على سبيل المثال 30 ثانية) حتى لا تتزايد التأخيرات إلى دقائق:

delay = min(cap, base * 2^retry_count)

يحل هذا مشكلة "الضغط" ولكنه لا يحل مشكلة التزامن. إذا فشل 5000 عميل في نفس اللحظة، فإن التراجع الأسي العادي يجعل جميع الـ 5000 يعودون في t=0.5 ثانية، ثم t=1 ثانية، ثم t=2 ثانية. لا تزال موجات. لا يزال قطيعًا، ولكنه أكثر تهذيبًا.

يعمل التذبذب على كسر التزامن عن طريق عشوائية التأخير. قام مدونة AWS Architecture بتحليل الأرقام في تحليل التراجع الأسي والتذبذب، محاكاة العملاء المتنافسين مقابل مورد متنازع عليه. لا يزال التراجع بدون تذبذب ينتج ارتفاعات متكتلة من الاستدعاءات. التذبذب الكامل، الذي يختار تأخيرًا عشوائيًا بين الصفر والسقف الأسي، أنتج أقل عدد من الاستدعاءات الإجمالية وأقصر أوقات الإنجاز تقريبًا:

delay = random_between(0, min(cap, base * 2^retry_count))

تُفاجئ هذه النتيجة الناس. يبدو عشوائية الطريق حتى الصفر غير دقيقة مقارنة بجدول مضاعفة منظم. لكن توزيع العملاء بشكل متساوٍ عبر النافذة هو بالضبط ما يحافظ على ثبات حمل الخادم. اختبر تحليل AWS أيضًا "التذبذب المتساوي" (نصف ثابت، نصف عشوائي) و "التذبذب غير المترابط"؛ وجاء التذبذب الكامل والتذبذب غير المترابط في المقدمة، والتذبذب الكامل هو الأبسط في الكتابة بشكل صحيح. استخدمه كنمط افتراضي لإعادة المحاولة ما لم تكن لديك قياسات تشير إلى خلاف ذلك.

احترم رأس Retry-After عندما يخبرك الخادم بذلك

التراجع هو تخمين عميلك للمدة التي يجب أن ينتظرها. أحيانًا يزيل الخادم هذا التخمين. يحمل رأس Retry-After، المحدد لردود 429 و 503، إما عددًا من الثواني أو تاريخ HTTP:

HTTP/1.1 429 Too Many Requests
Retry-After: 12

عندما يكون هذا الرأس موجودًا، فإنه يلغي التراجع المحسوب الخاص بك. يعلم الخادم متى تعاد ضبط نافذة حد المعدل أو متى ينتهي الصيانة؛ جدولك الأسي لا يعلم ذلك. تجاهل العملاء لـ Retry-After هو أحد الأسباب التي تجعل مقدمي الخدمة يصعدون من التقييد إلى الحظر الصريح. قم بتحليله، احترمه، وما زلت طبق حدك وأقصى عدد لإعادة المحاولة حتى لا يتمكن Retry-After: 86400 العدائي أو الذي يحتوي على خطأ من تعليق عامل الخدمة الخاص بك ليوم كامل.

الثباتية: الشرط المسبق لإعادة محاولة طلبات POST

هنا يكمن الفخ في 504 المذكور سابقًا. تُعد طلبات GET وPUT وDELETE ثابتة بموجب العقد: إرسالها مرتين يترك النظام في نفس الحالة. أما طلبات POST فليست كذلك. إذا انتهت مهلة `POST /v1/payments` بعد معالجتها بواسطة الخادم، فإن إعادة المحاولة الخاصة بك تنشئ دفعة ثانية. تهانينا، لقد أنشأت آلة تفرض رسومًا مزدوجة مع وقت تشغيل ممتاز.

الحل هو مفتاح الثباتية: معرّف فريد يتم إنشاؤه بواسطة العميل (عادةً ما يكون UUID) يُرسل كعنوان في كل عملية منطقية. يخزن الخادم المفتاح مع الاستجابة الأولى ويعيد تشغيل تلك الاستجابة المخزنة لأي تكرار. تعمل طلبات Stripe الثابتة بهذه الطريقة بالضبط، وقد اتبعتها معظم واجهات برمجة تطبيقات الدفع والتزويد.

قاعدتان تجعلان المفاتيح تعمل:

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

ميزانيات إعادة المحاولة وقواطع الدائرة: مخرج الطوارئ

يحدد التراجع متى تحدث عمليات إعادة المحاولة. ولا يحد من عدد مرات حدوثها. خلال انقطاع طويل، حتى العملاء الذين يعانون من تذبذب جيد يكدسون حمل إعادة المحاولة، وتتضاعف عمليات إعادة المحاولة المتراكبة: إذا أعادت بوابة واجهة برمجة التطبيقات الخاصة بك المحاولة 3 مرات وأعاد عميل خدمتك المحاولة 3 مرات، يمكن أن تصبح نقرة مستخدم واحدة 9 طلبات.

تحد آليتان من الضرر:

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

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

مثال جاهز للإنتاج بلغة بايثون

هنا النمط الكامل في مكان واحد: تصفية حالة قابلة لإعادة المحاولة، تذبذب كامل، دعم Retry-After، مفتاح ثبات، وحد أقصى صارم لإعادة المحاولة.

import random
import time
import uuid
import requests

RETRYABLE = {429, 502, 503, 504}
BASE = 0.5     # seconds
CAP = 30.0     # ceiling on any single delay
MAX_RETRIES = 5

def create_payment(payload):
    idempotency_key = str(uuid.uuid4())  # one key per logical payment
    headers = {"Idempotency-Key": idempotency_key}

    for retry_count in range(MAX_RETRIES + 1):
        try:
            resp = requests.post(
                "https://api.acmepay.com/v1/payments",
                json=payload, headers=headers, timeout=10,
            )
            if resp.status_code < 400:
                return resp.json()
            if resp.status_code not in RETRYABLE:
                resp.raise_for_status()  # 400/401/403/422: fail fast
            retry_after = resp.headers.get("Retry-After")
        except (requests.ConnectionError, requests.Timeout):
            retry_after = None  # network fault: fall through to backoff

        if retry_count == MAX_RETRIES:
            raise RuntimeError("payment failed after all retries")

        if retry_after and retry_after.isdigit():
            delay = min(CAP, float(retry_after))
        else:
            delay = random.uniform(0, min(CAP, BASE * 2 ** retry_count))
        time.sleep(delay)

الجدير بالملاحظة: يتم صياغة المفتاح مرة واحدة، خارج الحلقة. يتفوق Retry-After على التراجع المحسوب ولكنه لا يزال يحترم الحد الأقصى. تظهر الحالات غير القابلة لإعادة المحاولة على الفور. إذا كنت تعمل على جانب JavaScript، تمنحك مكتبة axios-retry نفس الشكل باستخدام خطافات `retryCondition` و `retryDelay`؛ يبقى جدول القرارات كما هو.

كيفية اختبار سلوك إعادة المحاولة قبل أن يفعله الإنتاج لك

تشحن معظم الفرق رمز إعادة المحاولة الذي لم يقم بتنفيذ فرع الفشل الخاص به ولو لمرة واحدة. تم اختبار المسار السعيد؛ ويتم تشغيل مسار 503 للمرة الأولى أثناء انقطاع حقيقي. يمكنك القيام بعمل أفضل باستخدام ميزتين من Apidog.

محاكاة الأعطال باستخدام خوادم وهمية. تتيح لك ميزة Apidog الذكية "mock" تعريف نقطة نهاية مثل `/v1/payments` وبرمجة استجاباتها. اجعلها تعيد 503 لأول استدعائين و 200 في الثالث، أو أعد 429 مع `Retry-After: 5`، أو أضف تأخيرًا لمدة 15 ثانية لتشغيل مهلة عميلك. وجه عميلك إلى عنوان URL الوهمي وشاهد حلقة إعادة المحاولة وهي تتعامل مع كل سيناريو، دون الحاجة إلى حادث إنتاج.

تأكيد سلوك العميل من خلال سيناريوهات الاختبار. تربط سيناريوهات اختبار Apidog الطلبات بتأكيدات وفحوصات التوقيت. قم بإنشاء سيناريو يتم تشغيله ضد الخادم الوهمي المعرض للخطأ الخاص بك وتأكد من أن الاستدعاء ينجح في النهاية، وأن الوقت الإجمالي المنقضي يقع ضمن النطاق المتوقع للتراجع، وأنه تم إنشاء مورد واحد بالضبط (مما يثبت أن مفتاح الثباتية قد أدى وظيفته). قم بتوصيل السيناريو بـ CI ويتم ممارسة منطق إعادة المحاولة الخاص بك في كل عملية التزام بدلاً من كل انقطاع.

هذا هو الفرق بين "أضفنا عمليات إعادة محاولة" و "تحققنا من أن عميلنا ينجو من تبعية محدودة المعدل، معطلة جزئيًا". قم بتنزيل Apidog مجانًا ويمكنك تشغيل خادم وهمي فاشل مقابل عميلك في حوالي عشر دقائق.

الأسئلة الشائعة

هل يجب أن أعيد محاولة 429؟

نعم، وهي الحالة الوحيدة التي يخبرك فيها الخادم عادةً بكيفية ذلك. اقرأ رأس Retry-After وانتظر على الأقل تلك المدة؛ ثم عد إلى التراجع الأسي مع التذبذب إذا كان الرأس مفقودًا. تعامل أيضًا مع 429 المتكررة كإشارة لإصلاح معدل طلباتك عن طريق التقييد من جانب العميل أو التخزين المؤقت، وليس كعملية عادية.

ما هو التذبذب الكامل (Full Jitter)؟

يختار التذبذب الكامل (Full Jitter) كل تأخير لإعادة المحاولة بشكل عشوائي ومتساوٍ بين الصفر والحد الأقصى الأسي: `random(0, min(cap, base * 2^n))`. إنه يمنع موجات إعادة المحاولة المتزامنة من العديد من العملاء. في محاكاة AWS، تفوق على التراجع العادي والتذبذب المتساوي من حيث إجمالي المكالمات التي تم إجراؤها والوقت اللازم للإكمال، وهذا هو السبب في أنه النمط الافتراضي في حزم تطوير برمجيات AWS (SDKs).

هل من الآمن إعادة محاولة طلبات POST؟

فقط عندما يكون الطلب ثابتًا في الممارسة، وهذا يعني لطلبات POST إرسال مفتاح ثبات يقوم الخادم بإزالة التكرار بناءً عليه. بدون هذا المفتاح، قد تؤدي إعادة المحاولة بعد انتهاء المهلة إلى تكرار دفعة، أو طلب، أو سجل، لأن الخادم ربما يكون قد عالج الطلب الذي تعتقد أنه فشل. تواجه وكلاء الذكاء الاصطناعي الذين يستدعون واجهات برمجة التطبيقات التي تكتب بيانات هذا باستمرار؛ أنماط استعادة الأخطاء لوكيل الذكاء الاصطناعي هي نفسها التي تم تناولها هنا: عمليات كتابة بمفاتيح، وإعادة محاولات محدودة، وقاطع دائرة.

كم مرة يجب أن أعيد المحاولة؟

ثلاث إلى خمس محاولات تعالج جميع الأعطال العابرة تقريبًا؛ بعد ذلك، تستقر معدلات النجاح بينما يستمر الحمل والوقت المستغرق في الازدياد. قم بإقران الحد الأقصى لكل طلب بميزانية إعادة محاولة عالمية (على سبيل المثال، قد تضيف عمليات إعادة المحاولة 10% من حركة المرور الإضافية) حتى لا يؤدي الانقطاع الكامل إلى مضاعفة حملك. إذا بقيت تبعية معطلة بعد آخر محاولة لإعادة المحاولة، فهذا يقع ضمن نطاق قاطع الدائرة، وليس نطاق إعادة المحاولة.

ممارسة تصميم API في Apidog

اكتشف طريقة أسهل لبناء واستخدام واجهات برمجة التطبيقات