سؤال

أثناء تحليل ملفات السجل، لاحظت أن حوالي 1% من مكالمات الخدمة انتهت بـ TimeoutException من جانب عميل Silverlight.الخدمات (wcf) بسيطة للغاية ولا تقوم بعمليات حسابية طويلة.وفقًا للسجل، تتم دائمًا معالجة جميع الاستدعاءات للخدمات في أقل من ثانية واحدة (حتى عند حدوث TimeoutException على العميل!)، لذلك لا يعد ذلك بمثابة مهلة للخادم.

إذن ما هو الخطأ؟يمكن أن يكون التكوين أو مشكلة في الشبكة؟كيف يمكنني تجنب ذلك؟ما هي معلومات التسجيل الإضافية التي يمكن أن تكون مفيدة لترجمة هذه المشكلة؟

الحل الوحيد الذي فكرت فيه هو إعادة محاولة مكالمات الخدمة بعد انتهاء المهلة.

وسوف أقدر أي مساعدة بشأن هذه المسألة!

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

تحديث: لم يحتوي سجل WCF على معلومات مفيدة حول هذه المشكلة.يبدو أن بعض مكالمات الخدمة لا تصل إلى جانب الخادم.

هل كانت مفيدة؟

المحلول

تكمن المشكلة في الحد الأقصى لعدد الاتصالات المتزامنة بخادم واحد في Internet Explorer 7/6.إنها 2 فقط! http://msdn.microsoft.com/en-us/library/cc304129(VS.85).aspx

إذا كان لدينا 3 مكالمات خدمة متزامنة (على سبيل المثال)، فسيتم إرسال اثنتين منها إلى الخادم على الفور ولكن الثالثة ستكون في قائمة الانتظار.كما أن مؤقت الإرسال (يتوافق مع sendTimeout) قيد التشغيل عندما يكون الطلب في قائمة الانتظار.إذا كان أول طلبين للخدمة سيتم تشغيلهما لفترة طويلة، فسيقوم الطلب الثالث بإنشاء TimeoutException على الرغم من أنه لم يتم إرساله إلى الخادم (ولن نرى أي معلومات حول هذا الطلب على جانب الخادم ولا يمكننا التقاطه باستخدام Fiddler ...).

في موقف أكثر واقعية، إذا كان لدينا حوالي 12 مكالمة متزامنة ومهلة إرسال افتراضية تبلغ دقيقة واحدة وإذا كانت معالجة مكالمات الخدمة أكثر من 10 ثوانٍ في المتوسط، فيمكننا بسهولة الحصول على استثناء المهلة مع المكالمتين الأخيرتين (12 / 2 * 10 ثانية = 60 ثانية ) لأنهم سوف ينتظرون كل الآخرين.

الحل هو:

  1. تقليل عدد مكالمات الخدمة المتزامنة.
  2. يزيد sendTimeout القيمة في عميل التكوين.
  3. تنفيذ ميزة إعادة المحاولة التلقائية للخدمات الهامة.
  4. تنفيذ قائمة انتظار الطلبات لإدارتها.

في حالتي قمت بـ 1-3 أشياء وكان ذلك كافيًا.

إليك تطبيقي لميزة إعادة المحاولة التلقائية:

public static class ClientBaseExtender
{
    /// <summary>
    /// Tries to execute async service call. If <see cref="TimeoutException"/> occured retries again.
    /// </summary>
    /// <typeparam name="TChannel">ServiceClient class.</typeparam>
    /// <typeparam name="TArgs">Type of service client method return argument.</typeparam>
    /// <param name="client">ServiceClient instance.</param>
    /// <param name="tryExecute">Delegate that execute starting of service call.</param>
    /// <param name="onCompletedSubcribe">Delegate that subcribes an event handler to the OnCompleted event of the service client method.</param>
    /// <param name="onCompleted">Delegate that executes when service call is succeeded.</param>
    /// <param name="onError">Delegate that executes when service call fails.</param>
    /// <param name="maxAttempts">Maximum attempts to execute service call before error if <see cref="TimeoutException"/> occured (by default 5).</param>
    public static void ExecuteAsyncRepeatedly<TChannel, TArgs>(this ClientBase<TChannel> client, Action tryExecute,
                                                               Action<EventHandler<TArgs>> onCompletedSubcribe, EventHandler<TArgs> onCompleted,
                                                               EventHandler<TArgs> onError, int maxAttempts)
        where TChannel : class
        where TArgs : AsyncCompletedEventArgs
    {
        int attempts = 0;
        var serviceName = client.GetType().Name;

        onCompletedSubcribe((s, e) =>
                                {
                                    if (e.Error == null) // Everything is OK
                                    {
                                        if (onCompleted != null)
                                            onCompleted(s, e);

                                        ((ICommunicationObject)client).Close();
                                        Debug.WriteLine("[{1}] Service '{0}' closed.", serviceName, DateTime.Now);
                                    }
                                    else if (e.Error is TimeoutException)
                                    {
                                        attempts++;

                                        if (attempts >= maxAttempts) // Final timeout after n attempts
                                        {
                                            Debug.WriteLine("[{2}], Final Timeout occured in '{0}' service after {1} attempts.", serviceName, attempts, DateTime.Now);

                                            if (onError != null)
                                                onError(s, e);
                                            client.Abort();

                                            Debug.WriteLine("[{1}] Service '{0}' aborted.", serviceName, DateTime.Now);
                                            return;
                                        }

                                        // Local timeout
                                        Debug.WriteLine("[{2}] Timeout occured in '{0}' service (attempt #{1}).", serviceName, attempts, DateTime.Now);

                                        Debug.WriteLine("[{2}] Attempt #{0} to execute call to '{1}' service.", attempts + 1, serviceName, DateTime.Now);
                                        tryExecute(); // Try again.
                                    }
                                    else
                                    {
                                        if (onError != null)
                                            onError(s, e);
                                        client.Abort();
                                        Debug.WriteLine("[{1}] Service '{0}' aborted.", serviceName, DateTime.Now);
                                    }
                                });

        Debug.WriteLine("[{2}] Attempt #{0} to execute call to '{1}' service.", attempts + 1, serviceName, DateTime.Now);
        tryExecute(); // First attempt to execute
    }
}

وهنا الاستخدام:

var client = new MyServiceClient();
client.ExecuteAsyncRepeatedly(() => client.MyOperationAsync(...),
    (EventHandler<MyOperationCompletedEventArgs> handler) => client.MyOperationCompleted += handler,
    (s, e) => // OnCompleted
        {
            Do(e.Result);
        },
    (s, e) => // OnError
        {
            HandleError(e.Error);
        }
);

نأمل أن تكون مفيدة.

نصائح أخرى

هل لا تزال تواجه هذه المشكلة؟

إذا كان الأمر كذلك، فربما يجب أن تشاهد الشبكة باستخدام شاشة Protller أو Microsoft Network Monitor أو أي شيء؟

MMMM ... هل يمكن أن يكون ذلك ممكنا أن يأخذ الطلب / الاستجابة أكثر من 64 ك أو الكثير من الأشياء المسلقة؟

يمكنك محاولة إنشاء محاكاة تدين الخادم مع تطبيق وحدة التحكم (فقط للتحقق مما إذا كانت شبكة، SL ...)؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top