كيف أتعامل مع فشل الرسالة في روابط MSMQ لـ WCF

StackOverflow https://stackoverflow.com/questions/82099

  •  09-06-2019
  •  | 
  •  

سؤال

لقد قمت بإنشاء خدمة WCF وأستخدم ربط netMsmqBinding.

هذه خدمة بسيطة تقوم بتمرير Dto إلى طريقة الخدمة الخاصة بي ولا تتوقع أي رد.يتم وضع الرسالة في MSMQ، وبمجرد التقاطها يتم إدراجها في قاعدة البيانات.

ما هي أفضل طريقة للتأكد من عدم فقدان أي بيانات.

لقد قمت بتجربة الطريقتين التاليتين:

  1. رمي استثناء

    يؤدي هذا إلى وضع الرسالة في قائمة انتظار الرسائل الميتة للاطلاع عليها يدويًا.يمكنني معالجة هذا عندما يبدأ جهدي

  2. قم بتعيين RetryRetryCount = "3" على الرابط

    بعد 3 محاولات - وهو ما يحدث على الفور، يبدو أن هذا يترك الرسالة في قائمة الانتظار، ولكنه يعيب خدمتي.تؤدي إعادة تشغيل الخدمة إلى تكرار هذه العملية.

من الناحية المثالية أود أن أفعل ما يلي:

حاول معالجة الرسالة

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

هل يمكنني تحقيق هذا؟إذا كان الأمر كذلك كيف؟هل يمكنك توجيهي إلى أي مقالات جيدة حول أفضل السبل لاستخدام WCF وMSMQ في المشهد الخاص بي.

أي مساعدة سيكون محل تقدير كبير.شكرًا!

بعض المعلومات الإضافية

أنا أستخدم MSMQ 3.0 على نظامي التشغيل Windows XP وWindows Server 2003.لسوء الحظ، لا يمكنني استخدام دعم الرسائل السامة المضمن الذي يستهدف MSMQ 4.0 وVista/2008.

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

المحلول

هناك نموذج في SDK قد يكون مفيدًا في حالتك.في الأساس، ما يفعله هو إرفاق تطبيق IErrorHandler بخدمتك والذي سيكتشف الخطأ عندما يعلن WCF أن الرسالة "سم" (أي.عند استنفاد كافة المحاولات التي تم تكوينها).ما يفعله النموذج هو نقل الرسالة إلى قائمة انتظار أخرى ثم إعادة تشغيل ServiceHost المرتبط بالرسالة (نظرًا لأنه سيكون قد حدث خطأ عند العثور على الرسالة السامة).

إنها ليست عينة جميلة جدًا، ولكنها يمكن أن تكون مفيدة.هناك بعض القيود، على الرغم من:

1- إذا كان لديك نقاط نهاية متعددة مرتبطة بخدمتك (أي.تم الكشف عنها من خلال عدة قوائم انتظار)، فلا توجد طريقة لمعرفة قائمة الانتظار التي وصلت إليها الرسالة السامة.إذا كان لديك قائمة انتظار واحدة فقط، فلن تكون هذه مشكلة.لم أر أي حل رسمي لهذا الأمر، ولكني قمت بتجربة بديل محتمل قمت بتوثيقه هنا: http://winterdom.com/weblog/2008/05/27/NetMSMQAndPoisonMessages.aspx

2- بمجرد نقل رسالة المشكلة إلى قائمة انتظار أخرى، تصبح مسؤوليتك، لذا يعود الأمر إليك في إعادتها إلى قائمة انتظار المعالجة بمجرد انتهاء المهلة (أو إرفاق خدمة جديدة بقائمة الانتظار هذه للتعامل معها).

لنكون صادقين، في كلتا الحالتين، أنت تنظر إلى بعض الأعمال "اليدوية" هنا التي لا تغطيها WCF بمفردها.

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

نصائح أخرى

أعتقد أنه مع MSMQ (المتوفر فقط على نظام التشغيل Vista) قد تتمكن من القيام بما يلي:

<bindings>
    <netMsmqBinding>
        <binding name="PosionMessageHandling"
             receiveRetryCount="3"
             retryCycleDelay="00:05:00"
             maxRetryCycles="3"
             receiveErrorHandling="Move" />
    </netMsmqBinding>
</bindings>

سيقوم WCF بإعادة المحاولة على الفور لعدد ReceiverRetryCount بعد فشل المكالمة الأولى.بعد فشل الدفعة ، يتم نقل الرسالة إلى قائمة انتظار إعادة المحاولة.بعد تأخير لمدة دقيقة RetryCycleDelay، يتم نقل الرسالة من قائمة انتظار إعادة المحاولة إلى قائمة انتظار نقطة النهاية وتتم إعادة محاولة الدفعة.سيتم تكرار هذا الوقت maxretrycycle.إذا فشلت كل ذلك ، يتم التعامل مع الرسالة وفقًا لـ SetserrorHandling والتي يمكن أن تتحرك (إلى قائمة انتظار السم) ، أو ترفض ، أو إسقاط أو خطأ

بالمناسبة، النص الجيد حول WCF وMSMQ هو الفصل التاسع من كتاب Progammig WCF من Juval Lowy

إذا كنت تستخدم SQL-Server، فيجب عليك استخدام معاملة موزعة، حيث يدعمها كل من MSMQ وSQL-Server.ما يحدث هو أنك تقوم بلف قاعدة بياناتك وكتابتها في كتلة TransactionScope واستدعاءscope.Complete() فقط في حالة نجاحها.إذا فشلت، فعندما يعود أسلوب WCF الخاص بك، سيتم وضع الرسالة مرة أخرى في قائمة الانتظار لتتم تجربتها مرة أخرى.إليك نسخة مشذبة من الكود الذي أستخدمه:

    [OperationBehavior(TransactionScopeRequired=true, TransactionAutoComplete=true)]
    public void InsertRecord(RecordType record)
    {
        try
        {
            using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
            {
                SqlConnection InsertConnection = new SqlConnection(ConnectionString);
                InsertConnection.Open();

                // Insert statements go here

                InsertConnection.Close();

                // Vote to commit the transaction if there were no failures
                scope.Complete();
            }
        }
        catch (Exception ex)
        {
            logger.WarnException(string.Format("Distributed transaction failure for {0}", 
                Transaction.Current.TransactionInformation.DistributedIdentifier.ToString()),
                ex);
        }
     }

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

يتمتع مدير المعاملات الموزعة بحضور محيط، وعندما تقوم بإنشاء مثيل جديد لـ TransactionScope فإنه يبحث تلقائيًا عن المعاملة الحالية ضمن نطاق استدعاء الطريقة - والتي كان من المفترض أن يتم إنشاؤها بالفعل بواسطة WCF عندما أخرجت الرسالة من قائمة الانتظار واستدعى طريقتك.

لسوء الحظ، أنا عالق في نظامي التشغيل Windows XP وWindows Server 2003، لذا فإن هذا ليس خيارًا بالنسبة لي.- (سأعيد توضيح ذلك في سؤالي لأنني وجدت هذا الحل بعد النشر وأدركت أنني لا أستطيع استخدامه)

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

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

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

حسنًا، كان لديك الإجابة المثالية لـ MSMQ 4.0، ولكن لسوء الحظ ليس بالنسبة لي

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