سياسة إعادة المحاولة في تطوير التطبيقات المحمولة — الجوهر والاستراتيجيات والمبادئ

المؤلف: IT Sectr نُشر: 2026-03-11 وقت القراءة: 10 دق

سياسة إعادة المحاولة — هي مجموعة من القواعد التي تحدد متى وكيف يعيد التطبيق المحمول محاولة استدعاءات الشبكة الفاشلة تلقائياً. مع الاتصالات غير المستقرة أو أخطاء الخادم المؤقتة، تعمل سياسة إعادة المحاولة المصممة جيداً على تحسين موثوقية التطبيق دون تدخل المستخدم. وفقاً لبحث أجرته Google Developer Relations (2025)، يقلل التنفيذ الصحيح لسياسة إعادة المحاولة من نسبة الطلبات المفقودة بنسبة 40–60% في التطبيقات المحمولة ذات عمليات الشبكة المتكررة.

النقاط الرئيسية

  • سياسة إعادة المحاولة — استراتيجية لإعادة المحاولة التلقائية للطلبات عند فشل الشبكة أو أخطاء الخادم المؤقتة.
  • التباعد الأسي — طريقة لزيادة التأخير بين إعادة المحاولات لتقليل الحمل على الخادم.
  • Jitter — انحراف عشوائي في التأخير يمنع تأثير التزاحم (thundering herd).
  • التناضح — شرط أساسي لإعادة المحاولة الآمنة: يجب ألا يتسبب الطلب المتكرر في آثار جانبية.
  • قاطع الدائرة — آلية لإيقاف إعادة المحاولة أثناء عدم توفر الخدمة لفترة طويلة للحفاظ على الموارد.

ما هي سياسة إعادة المحاولة؟

سياسة إعادة المحاولة — هي استراتيجية برمجية تحدد سلوك العميل عند فشل طلب الشبكة: أي الأخطاء يجب إعادة محاولتها، وكم مرة، وبأي تأخير، ومتى تتوقف عن المحاولة. في التطبيقات المحمولة، تعتبر سياسة إعادة المحاولة مهمة بشكل حاسم بسبب عدم استقرار الشبكات المحمولة والأعطال المؤقتة المحتملة على جانب الخادم.

تتضمن سياسة إعادة المحاولة الأساسية ثلاث معلمات: الحد الأقصى لعدد إعادة المحاولات (maxRetries)، التأخير الأولي (baseDelay)، واستراتيجية التباعد (backoff strategy). بالإضافة إلى ذلك، يمكن تحديد قائمة رموز حالة HTTP التي يجب أن تؤدي إلى إعادة محاولة، ومهلة زمنية لإحباط جميع المحاولات.

وفقاً لكتاب «Designing Data-Intensive Applications» لمارتن كليبنمان، 50% من الأعطال في الأنظمة الموزعة مؤقتة ويتم حلها بإعادة المحاولة. وهذا يجعل سياسة إعادة المحاولة واحدة من أكثر الطرق فعالية وغير مكلفة لتحسين تحمل الأعطال في التطبيقات المحمولة دون تغييرات في بنية الخادم.

ما الأخطاء التي يجب إعادة محاولتها

الأخطاء المؤقتة (القابلة لإعادة المحاولة) هي النوع الوحيد من الأعطال التي يجب أن تستجيب لها سياسة إعادة المحاولة. تشمل مهلات الاتصال (SocketTimeoutException)، وعدم توفر الخادم مؤقتاً (HTTP 503, 502)، وأخطاء DNS. الأخطاء الدائمة — HTTP 400, 401, 403, 404 — لا معنى لإعادة محاولتها لأنها تشير إلى مشكلة في الطلب وليس في الشبكة أو الخادم.

وفقاً لمدونة AWS Architecture Blog، فإن التصنيف الصحيح للأخطاء إلى قابلة لإعادة المحاولة وغير قابلة لإعادة المحاولة هو أهم قرار عند تصميم سياسة إعادة المحاولة. إعادة محاولة طلب غير متضامن مع HTTP 401 قد تؤدي إلى حظر الحساب، وإعادة محاولة HTTP 400 قد تؤدي إلى إنشاء بيانات مكررة. قم دائماً بتكوين قائمة الرموز المراد إعادة محاولتها بشكل صريح.

استراتيجيات إعادة المحاولة الأساسية

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

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

الاستراتيجيةصيغة التأخيرالوقت التراكمي (3 محاولات)التطبيق
ثابتdelay = D3 × Dسيناريوهات بسيطة، مهلات محلية
متزايدdelay = N × D6 × Dتخفيف تدريجي للحمل
أسيdelay = D × 2^N7 × Dأعطال جماعية، خدمات سحابية
أسي + Jitterdelay = random(0, D × 2^N)متغيرحمولة عالية، خدمات مصغرة

يعتمد اختيار الاستراتيجية على طبيعة التطبيق. لمهام الخلفية لمزامنة البيانات على الأجهزة المحمولة، تعتبر الاستراتيجية الأسية مع jitter هي الأمثل — فهي توفر أعلى احتمالية للنجاح مع أقل حمل على الخادم وجهاز المستخدم.

التباعد الأسي و Jitter

التباعد الأسي — استراتيجية يتضاعف فيها التأخير بين إعادة المحاولات مع كل محاولة. إذا كان التأخير الأولي ثانية واحدة، فسيكون تسلسل التأخير 1، 2، 4، 8، 16 ثانية. وهذا يعطي الخادم وقتاً متزايداً أسياً للتعافي.

Jitter — انحراف عشوائي في التأخير يمنع طلبات إعادة المحاولة المتزامنة من عدة عملاء (مشكلة التزاحم). بدون jitter، سيعيد ألف عميل بنفس سياسة إعادة المحاولة محاولة الطلبات في وقت واحد، مما يخلق حملاً ذروياً على الخادم. يعمل Jitter على توزيع إعادة المحاولات على مدى زمني.

التنفيذ في Kotlin مع coroutines

تسمح coroutines في Kotlin بتنفيذ التباعد الأسي مع jitter دون حظر الخيط الرئيسي. تقبل الدالة retry من kotlinx-coroutines شرط إعادة المحاولة وكتلة تحتوي على جسم الطلب، وتدير التأخيرات وعدد المحاولات تلقائياً.

kotlin
suspend fun RetryPolicy.executeWithRetry(
    block: suspend () -> Result<T>
): Result<T> {
    var lastError: Throwable? = null
    repeat(maxRetries + 1) { attempt ->
        try {
            return block()
        } catch (e: Exception) {
            if (!isRetriable(e) || attempt == maxRetries) {
                return Result.failure(e)
            }
            val delay = (baseDelayMs * (1 shl attempt))
                .toLong()
            val jitteredDelay = (delay * (0.5 + Random.nextDouble())).toLong()
            delay(jitteredDelay)
            lastError = e
        }
    }
    return Result.failure(lastError!!)
}

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

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

قاطع الدائرة — نمط تصميم يمنع طلبات إعادة المحاولة اللانهائية أثناء عدم توفر الخدمة لفترة طويلة. عندما يتجاوز عدد الأخطاء حداً معيناً، ينتقل قاطع الدائرة إلى الحالة OPEN ويعيد خطأ فوراً دون تنفيذ الطلب، مما يعطي الخادم وقتاً للتعافي.

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

آلة حالات قاطع الدائرة

لقاطع الدائرة ثلاث حالات: CLOSED (تشغيل عادي، يتم تنفيذ الطلبات)، OPEN (عطل، يتم حظر الطلبات)، و HALF_OPEN (طلب تجريبي للتحقق من التعافي). بعد مهلة زمنية محددة في الحالة OPEN، ينتقل القاطع إلى HALF_OPEN وينفذ طلباً واحداً — عند النجاح يعود إلى CLOSED، وعند الفشل إلى OPEN.

kotlin
class CircuitBreaker(
    private val failureThreshold: Int = 3,
    private val timeoutMs: Long = 30000
) {
    private var state = State.CLOSED
    private var failureCount = 0
    private var lastFailureTime: Long = 0

    suspend fun T.protect(block: suspend () -> T): T {
        checkState()
        return try {
            val result = block()
            onSuccess()
            result
        } catch (e: Exception) {
            onFailure()
            throw e
        }
    }
}

يحتوي تنفيذ قاطع الدائرة في Kotlin على عداد أخطاء ومؤقت تعافي. تتحقق طريقة protect من الحالة الحالية قبل تنفيذ الطلب المغلّف وتحدّث عداد الأخطاء عند الفشل. بعد الوصول إلى failureThreshold، يتم رفض جميع الطلبات فوراً حتى انتهاء timeoutMs.

سياسة إعادة المحاولة في التطبيقات المحمولة

الشبكات المحمولة لها خصائص تجعل سياسة إعادة المحاولة مهمة بشكل خاص. التبديل بين Wi-Fi والبيانات المحمولة، فقدان الإشارة في مترو الأنفاق والأنفاق، الحظر المؤقت على مستوى المشغل — كل هذه السيناريوهات تؤدي إلى فشل الطلبات التي يمكن معالجتها بنجاح عن طريق إعادة المحاولة.

على Android، توفر مكتبة Retrofit و OkHttp آلية إعادة محاولة مدمجة من خلال Interceptor. على iOS، يتم حل المهمة عبر URLSessionConfiguration والتفويض المخصص. للتطوير عبر المنصات، يتضمن Ktor (KMP) دعماً مدمجاً لإعادة المحاولة باستراتيجيات قابلة للتكوين.

التنفيذ على iOS مع Combine

Combine — إطار عمل Apple للبرمجة التفاعلية. يكرر عامل retry في Combine publisher عدداً محدداً من المرات عند حدوث خطأ، لكنه لا يسمح بتكوين التأخير بين إعادة المحاولات. لسياسة إعادة محاولة كاملة، يتم استخدام مزيج مخصص من catch و flatMap مع تأخير.

swift
extension Publisher {
    func retryWithBackoff(
        retries: Int = 3,
        baseDelay: TimeInterval = 1.0
    ) -> AnyPublisher<Output, Failure> {
        return self.catch { error -> AnyPublisher in
            guard retries > 0 else {
                return Fail(error).eraseToAnyPublisher()
            }
            return Just(())
                .delay(for: .seconds(baseDelay), scheduler: DispatchQueue.main)
                .flatMap { self.retryWithBackoff(
                    retries: retries - 1,
                    baseDelay: baseDelay * 2
                ) }
                .eraseToAnyPublisher()
        }
        .eraseToAnyPublisher()
    }
}

يقوم امتداد retryWithBackoff لـ Publisher في Combine بتنفيذ التباعد الأسي من خلال استدعاءات متكررة مع تناقص العداد ومضاعفة التأخير. يُنشئ عامل delay فاصل زمني بين إعادة المحاولات، بينما يعترض catch الخطأ ويقرر ما إذا كان سيعيد المحاولة أو يعيد الفشل.

الأخطاء الشائعة في سياسة إعادة المحاولة

الخطأ الأول — إعادة محاولة الطلبات دون التحقق من التناضح. إذا قام الخادم بإنشاء مورد ولكن لم يُرجع تأكيداً بسبب فشل الشبكة، فإن إعادة المحاولة ستُنشئ نسخة مكررة. لطلبات POST، استخدم دائماً مفتاح التناضح (Idempotency-Key) في الترويسة أو انتقل إلى إعادة المحاولة فقط لـ GET و PUT و DELETE.

الخطأ الثاني — إعادة المحاولة إلى ما لا نهاية. حدد دائماً عدداً أقصى من المحاولات (3–5 للتطبيقات المحمولة) ومهلة زمنية إجمالية لجميع المحاولات. إعادة المحاولة اللانهائية تستهلك البطارية وتُنشئ حملاً طفيلياً على الخادم، خاصة أثناء ترحيل قواعد البيانات أو تغييرات API.

الخطأ الثالث — تجاهل سياق التطبيق. إذا أغلق المستخدم التطبيق أو انتقل إلى الخلفية، يجب إلغاء سياسات إعادة المحاولة النشطة بشكل صحيح. استخدم coroutines مع SupervisorScope أو Combine مع دورة حياة واجهة المستخدم للإلغاء التلقائي لإعادة المحاولة عند إغلاق الشاشة.

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

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

كم مرة يجب إعادة محاولة الطلب في التطبيق المحمول؟

العدد الأمثل لـ إعادة المحاولات هو 3–5 محاولات لمعظم السيناريوهات. للمزامنة في الخلفية، 5–7 محاولات مقبولة؛ للطلبات التفاعلية (مثل إرسال نموذج)، لا يزيد عن 3. عدد أكبر من إعادة المحاولات لا يزيد من احتمالية النجاح ولكنه يستهلك بطارية المستخدم وبياناته.

ما هو التباعد الأسي بكلمات بسيطة؟

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

ما رموز حالة HTTP التي يجب إعادة محاولتها؟

أعد محاولة فقط الأخطاء المؤقتة: 408 (Request Timeout)، 429 (Too Many Requests)، 502 (Bad Gateway)، 503 (Service Unavailable)، 504 (Gateway Timeout). الأخطاء 4xx (باستثناء 408 و 429) تشير إلى مشاكل في العميل — إعادة محاولتها لا معنى لها وقد تكون خطيرة على بيانات المستخدم.

ما الفرق بين سياسة إعادة المحاولة وقاطع الدائرة؟

سياسة إعادة المحاولة تدير إعادة محاولة طلب واحد عند الفشل. قاطع الدائرة يدير حالة الاتصال بالخدمة: عند تراكم الأخطاء، يفتح الدائرة (OPEN) ويمنع الطلبات الجديدة. تعمل إعادة المحاولة على مستوى الاستدعاء الفردي، بينما يعمل قاطع الدائرة على مستوى التكامل مع الخدمة.

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

لـ اختبار سياسة إعادة المحاولة، استخدم NetworkInterceptor (OkHttp) على Android و URLProtocol (URLSession) على iOS لمحاكاة أعطال الشبكة. حدد المعلمات: تواتر الأخطاء، مدة عدم التوفر، ورموز الاستجابة. تتحقق اختبارات الوحدة مع MockWebServer (OkHttp) أو OHHTTPStubs (iOS) من منطق إعادة المحاولة دون شبكة حقيقية.

الخلاصة

  • سياسة إعادة المحاولة — استراتيجية لإعادة المحاولة التلقائية لطلبات الشبكة عند الأعطال المؤقتة مع معلمات قابلة للتكوين للتأخير وعدد المحاولات.
  • التباعد الأسي مع jitter — الاستراتيجية الأساسية للتطبيقات المحمولة، تقلل الحمل على الخادم أثناء الأعطال الجماعية وتمنع تأثير التزاحم.
  • التناضح — شرط إلزامي لإعادة المحاولة الآمنة للطلبات غير GET: بدونها، تُنشئ إعادة المحاولة بيانات مكررة أو آثار جانبية غير مرغوب فيها.
  • قاطع الدائرة يكمل سياسة إعادة المحاولة بمنع إعادة المحاولة اللانهائية أثناء عدم توفر الخدمة لفترة طويلة والحفاظ على موارد الجهاز.
  • تصنيف الأخطاء إلى قابلة لإعادة المحاولة (503, 502, timeout) وغير قابلة لإعادة المحاولة (400, 401, 403) أمر بالغ الأهمية للتشغيل الصحيح لسياسة إعادة المحاولة.
  • الحد الأقصى 3–5 إعادة محاولات في السيناريوهات التفاعلية وحتى 7 للمزامنة في الخلفية — القيمة المثلى للتطبيقات المحمولة وفقاً لـ Google Developer Relations.
  • توصية — قم بتنفيذ سياسة إعادة المحاولة مع التباعد الأسي وقاطع الدائرة والتسجيل لجميع طلبات الشبكة في التطبيقات المحمولة.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا