أنماط إعادة المحاولة والتراجع لتكاملات 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 عبر جميع نقاط النهاية. توصيات إعادة المحاولة الكاملة موجودة في مستند التهيئة بعد طلب الوصول.
