منصة B2B للسلع الرقمية

أنماط إعادة المحاولة والتراجع لتكاملات B2B لعام 2026

أنماط إنتاجية لإعادة المحاولة والتراجع في تكاملات B2B — أُسّي مع تشويش، قاطع دائرة، وأمثلة برمجية بثلاث لغات مع إرشادات خاصة بـ FoxReload.

أنماط إعادة المحاولة والتراجع لتكاملات B2B لعام 2026 — Polly وaxios-retry وtenacity

إعادة المحاولة ليست مجرد "لفّ في try/catch داخل حلقة while". تعتمد الاستراتيجية الصحيحة على فئة الخطأ، وعلى اتفاقية مستوى الخدمة المتوقعة للخدمة الأعلى، وعلى الحمل الحالي أثناء التعافي. هذه المقالة مرجع عملي لأنماط إعادة المحاولة في تكاملات B2B، مع أمثلة برمجية إنتاجية وإرشادات خاصة بـ FoxReload.

1. الصيغة الأساسية — أُسّي + تشويش

function delay(attempt: number, baseMs = 500, capMs = 30000): number {
  const exp = Math.min(baseMs * Math.pow(2, attempt), capMs);
  const jitter = Math.random() * exp * 0.5; // ±50%
  return exp / 2 + jitter; // "full jitter" AWS-style
}
// attempt 0: 250–750ms
// attempt 1: 500–1500ms
// attempt 2: 1000–3000ms
// attempt 5: 8000–24000ms

"التشويش الكامل" (أعلاه) موصى به من AWS، ويخفّض ذروة حمل التعافي بشكل أفضل من "التشويش المتساوي".

2. متى نعيد المحاولة ومتى لا نعيدها

حالة HTTP قابلة لإعادة المحاولة؟ ملاحظة
5xx نعم عابر من جهة الخادم
429 نعم احترم ترويسة Retry-After إن وُجدت
408 نعم مهلة العميل
4xx (الباقي) لا خطأ عميل دائم
خطأ شبكة نعم DNS، إعادة ضبط الاتصال، TLS
مهلة على GET نعم آمن — GET متكرر آمن
مهلة على POST خاص راجع القسم 3

3. حالة خاصة — مهلة POST /api/orders

لا تملك FoxReload مفاتيح تكرار آمنة. قد يكون POST /api/orders الذي انتهت مهلته قد أنشأ طلبًا قيد المعالجة الآن. قبل إعادة محاولة POST —

async function safeRetryOrder(
  items: OrderItem[],
  apiKey: string,
  attempt: number,
): Promise<Order> {
  if (attempt > 0) {
    // Check if a previous attempt already created the order
    const recent = await fetch('https://public-api.foxreload.com/api/orders?limit=10', {
      headers: { 'X-API-Key': apiKey },
    }).then(r => r.json());
    const match = recent.orders?.find((o: Order) =>
      o.items.some(i => i.product.id === items[0].itemId) &&
      Date.now() - new Date(o.createdAt).getTime() < 120_000,
    );
    if (match) return match; // order exists — do not create again
  }
  return fetch('https://public-api.foxreload.com/api/orders', {
    method: 'POST',
    headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
    body: JSON.stringify({ items }),
  }).then(r => r.json());
}

4. axios-retry (Node.js)

import axios from 'axios';
import axiosRetry from 'axios-retry';

const client = axios.create({
  baseURL: 'https://public-api.foxreload.com',
  timeout: 30000,
  headers: { 'X-API-Key': process.env.FOXRELOAD_KEY },
});

axiosRetry(client, {
  retries: 5,
  retryDelay: (count) => axiosRetry.exponentialDelay(count) + Math.random() * 1000,
  retryCondition: (err) => {
    if (!err.response) return true; // network
    const s = err.response.status;
    // Do NOT automatically retry POST — handle separately
    if (err.config?.method === 'post') return false;
    return s >= 500 || s === 429 || s === 408;
  },
  shouldResetTimeout: true,
});

إعداد shouldResetTimeout: true ضروري — فبدونه تستهلك مهلة واحدة مدتها 30 ثانية كامل ميزانية إعادة المحاولة.

5. tenacity (Python)

from tenacity import retry, stop_after_attempt, wait_exponential_jitter, retry_if_exception_type
import httpx

@retry(
    stop=stop_after_attempt(6),
    wait=wait_exponential_jitter(initial=0.5, max=30, jitter=2),
    retry=retry_if_exception_type((httpx.TransportError, httpx.HTTPStatusError)),
    reraise=True,
)
def get_order_status(order_id, api_key):
    r = httpx.get(
        f'https://public-api.foxreload.com/api/orders/{order_id}',
        headers={'X-API-Key': api_key},
        timeout=30,
    )
    if r.status_code >= 500 or r.status_code == 429:
        r.raise_for_status()
    return r.json()

بالنسبة لـ POST /api/orders، طبّق نمط التحقق من الحالة قبل إعادة المحاولة يدويًا بدلًا من استخدام مُزخرِف إعادة محاولة عام.

6. Polly (C#/.NET)

var policy = Policy
    .HandleResult<HttpResponseMessage>(r => (int)r.StatusCode >= 500 || (int)r.StatusCode == 429)
    .Or<HttpRequestException>()
    .WaitAndRetryAsync(5, attempt =>
        TimeSpan.FromMilliseconds(500 * Math.Pow(2, attempt)
            + Random.Shared.Next(0, 500)));

var circuit = Policy
    .HandleResult<HttpResponseMessage>(r => (int)r.StatusCode >= 500)
    .CircuitBreakerAsync(5, TimeSpan.FromSeconds(60));

var combined = Policy.WrapAsync(circuit, policy);
// Use for GET requests only; handle POST /api/orders separately
var resp = await combined.ExecuteAsync(() =>
    client.GetAsync($"/api/orders/{orderId}"));

7. قاطع الدائرة — عندما لا تعود إعادة المحاولة مجدية

إذا أخفقت الخدمة الأعلى بنسبة 50% فأكثر لمدة 30 ثانية، افتح القاطع وافشل سريعًا —

import CircuitBreaker from 'opossum';
const breaker = new CircuitBreaker(callFoxreload, {
  errorThresholdPercentage: 50,
  resetTimeout: 60_000,
  rollingCountTimeout: 30_000,
});
breaker.fallback(() => ({ fromCache: true }));

هذا يحمي المكونات الأدنى من انهيار إعادة المحاولة أثناء الانقطاع.

CTA

تُرجِع واجهة FoxReload ترويسة Retry-After عند 429، وتستخدم HTTPS عبر جميع نقاط النهاية. توصيات إعادة المحاولة الكاملة موجودة في مستند التهيئة بعد طلب الوصول.

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

كم عدد محاولات إعادة المحاولة الأمثل لواجهة B2B؟
من 5 إلى 8 محاولات بميزانية إجمالية أقل من 5 دقائق. الأكثر يجعل العميل يرى مهلة انتهاء، والأقل يفقدك الطلبات عند الأخطاء العابرة. لنقاط النهاية الفورية (الكتالوج والمنتجات) — 3 محاولات خلال 30 ثانية.
ما الذي يجب ألا أعيد المحاولة معه أبدًا؟
أي خطأ 4xx باستثناء 408 (مهلة) و429 (حدّ المعدل). أخطاء 400 طلب خاطئ و401 غير مصرّح و403 ممنوع و404 غير موجود و422 خطأ تحقق هي أخطاء دائمة. إعادة المحاولة معها حلقة لا نهائية دون أي فرصة للنجاح.
لماذا نضيف التشويش إلى التراجع؟
بدون تشويش يعيد كل عميل المحاولة متزامنًا (عند 30 و60 و120 ثانية) — قطيع جامح أثناء التعافي. التشويش بنسبة ±30% يوزّع موجات إعادة المحاولة على 25 إلى 35 ثانية و50 إلى 70 ثانية و100 إلى 140 ثانية، ويخفّض ذروة الحمل عند التعافي بمقدار 4 إلى 6 أضعاف.
هل قاطع الدائرة مفيد إن كانت لديّ إعادة محاولة بالفعل؟
نعم. إعادة المحاولة تعالج الأخطاء المعزولة (وميض الشبكة). أما قاطع الدائرة فيعالج الانقطاعات المستمرة — بعد 50% من الإخفاقات خلال 30 ثانية يُفتح القاطع، فتفشل الطلبات اللاحقة فورًا ولا تُحمّل الخدمة الأعلى. التعافي — استكشاف نصف مفتوح خلال 60 ثانية.
كيف أعيد محاولة POST /api/orders بأمان بعد المهلة؟
لا تملك FoxReload مفاتيح تكرار آمنة. قبل إعادة المحاولة، استدعِ GET /api/orders?limit=10 للتحقق مما إذا كان الطلب قد أُنشئ. إن وُجد، استطلعه. وإن لم يوجد، أعد POST. لا تُعد أبدًا POST إلى /api/orders بشكل أعمى — فستنشئ طلبات مكررة وتُحاسَب مرتين.
احصل على وصول واجهة FoxReload

مقالات ذات صلة