أفضل الممارسات لتنفيذ العمليات المتسلسلة

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

  •  03-07-2019
  •  | 
  •  

سؤال

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

يجب أن تمر جميع المهام:

SendEmail ArchivereportSindatabase Createfile

في السيناريو أعلاه، يجب أن تمر جميع المهام وإلا يجب أن يتم التراجع عن العملية الدفعية بأكملها.

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

المحلول

والاستثناءات هي في العموم جيد لهذا النوع من الشيء. شبه جافا / جافا سكريبت / C ++ كود:

try {
    if (!SendEmail()) {
        throw "Could not send e-mail";
    }

    if (!ArchiveReportsInDatabase()) {
        throw "Could not archive reports in database";
    }

    if (!CreateAFile()) {
        throw "Could not create file";
    }

    ...

} catch (Exception) {
    LogError(Exception);
    ...
}

والأفضل من ذلك إذا أساليب الاتصال رمي الاستثناءات أنفسهم:

try {
    SendEmail();
    ArchiveReportsInDatabase();
    CreateAFile();
    ...

} catch (Exception) {
    LogError(Exception);
    ...
}

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

وعلاوة على ذلك، لديك نقطة واحدة في رمز لمعالجة الأخطاء، وقطع الأشجار، التراجع الخ.

نصائح أخرى

عمليات التراجع صعبة - AFAIK، هناك طريقتان فقط للقيام بذلك.إما أ بروتوكول الالتزام بمرحلتين, ، أو المعاملات التعويضية.عليك حقًا أن تجد طريقة لتنظيم مهامك بإحدى هذه الموضات.

عادةً ما تكون الفكرة الأفضل هي الاستفادة من العمل الشاق الذي يقوم به الأشخاص الآخرون واستخدام التقنيات التي تحتوي بالفعل على جهازي كمبيوتر أو تعويض مدمج.وهذا أحد أسباب شهرة نظام RDBMS.

لذا، فإن التفاصيل تعتمد على المهمة...ولكن النمط سهل إلى حد ما:

class Compensator {
   Action Action { get; set; }
   Action Compensate { get; set; }
}

Queue<Compensator> actions = new Queue<Compensator>(new Compensator[] { 
   new Compensator(SendEmail, UndoSendEmail),
   new Compensator(ArchiveReportsInDatabase, UndoArchiveReportsInDatabase),
   new Compensator(CreateAFile, UndoCreateAFile)
});

Queue<Compensator> doneActions = new Queue<Compensator>();
while (var c = actions.Dequeue() != null) {
   try {
      c.Action();
      doneActions.Add(c);
   } catch {
      try {
        doneActions.Each(d => d.Compensate());
      } catch (EXception ex) {
        throw new OhCrapException("Couldn't rollback", doneActions, ex);
      }
      throw;
   }
}

بالطبع، بالنسبة لمهامك المحددة، قد تكون محظوظًا.

  • من الواضح أنه يمكن بالفعل تغليف عمل RDBMS في معاملة.
  • إذا كنت تستخدم نظام التشغيل Vista أو Server 2008، فستحصل على المعاملات NTFS لتغطية سيناريو CreateFile الخاص بك.
  • يعد البريد الإلكتروني أكثر تعقيدًا بعض الشيء - لا أعرف أي جهازي كمبيوتر أو معوضات من حوله (سأفاجأ قليلاً إذا أشار شخص ما إلى أن Exchange لديه واحد، على الرغم من ذلك) لذلك ربما سأستخدمه MSMQ لكتابة إشعار والسماح للمشترك باستلامه وإرساله بالبريد الإلكتروني في النهاية.عند هذه النقطة، تغطي معاملتك فقط إرسال الرسالة إلى قائمة الانتظار، ولكن ربما يكون هذا جيدًا بما فيه الكفاية.

كل هؤلاء يمكنهم المشاركة في النظام.المعاملات الصفقة، لذا يجب أن تكون في حالة جيدة جدًا.

وفي C #

والعودة SendEmail () && ArchiveResportsInDatabase () && CreateAFile ()؛

وهناك فكرة أخرى:

try {
    task1();
    task2();
    task3();
    ...
    taskN();
}
catch (TaskFailureException e) {
    dealWith(e);
}

بضعة اقتراحات:

في السيناريو الموزع، قد تكون هناك حاجة إلى نوع من بروتوكول الالتزام على مرحلتين.بشكل أساسي، تقوم بإرسال رسالة لجميع المشاركين تقول "الاستعداد للقيام بـ X".يجب على كل مشارك بعد ذلك إرسال رد يقول "حسنًا ، أضمن أنني أستطيع أن أفعل X" أو "لا ، لا يمكنني فعل ذلك". إذا ضمان جميع المشاركين أن يتمكنوا من إكمالهم ، فقم بإرسال الرسالة التي تخبرهم بالقيام بذلك.يمكن أن تكون "الضمانات" صارمة حسب الحاجة.

هناك طريقة أخرى تتمثل في توفير نوع من آلية التراجع لكل عملية، ثم يكون لديك منطق مثل هذا:

try:
    SendEmail()
    try:
        ArchiveReportsInDatabase()
        try:
             CreateAFile()
        except:
            UndoArchiveReportsInDatabase()
            raise
    except:
        UndoSendEmail()
        raise
except:
    // handle failure

(لن ترغب في أن يبدو الكود الخاص بك هكذا؛هذا مجرد توضيح لكيفية تدفق المنطق.)

إذا كانت لغتك تسمح بذلك، فهذا مرتب جدًا:

  1. ضع مهامك في مجموعة من كتل التعليمات البرمجية أو المؤشرات الوظيفية.
  2. التكرار على المصفوفة.
  3. كسر إذا كانت أي كتلة ترجع الفشل.

لم تذكر لغة البرمجة/البيئة التي تستخدمها.إذا كان هو .NET Framework، قد ترغب في إلقاء نظرة عليه هذا المقال.فهو يصف التزامن والتحكم في وقت التشغيل من Microsoft Robotics Studio، والذي يسمح لك بتطبيق جميع أنواع القواعد على مجموعة من الأحداث (غير المتزامنة):على سبيل المثال، يمكنك الانتظار حتى يكتمل أي عدد منها، أو الإلغاء في حالة فشل حدث واحد، وما إلى ذلك.يمكنه تشغيل الأشياء في سلاسل رسائل متعددة أيضًا، بحيث تحصل على طريقة قوية جدًا للقيام بالأشياء.

وأنت لم تحدد بيئتك. في يونكس قذيفة البرمجة، مشغل && لا فقط هذا.

SendEmail () {
  # ...
}
ArchiveReportsInDatabase () {
  # ...
}
CreateAFile () {
  # ...
}

SendEmail && ArchiveReportsInDatabase && CreateAFile

إذا كنت تستخدم اللغة التي تستخدم النوع دائرة تقييم (جافا وC # تفعل)، يمكنك القيام به ببساطة:

return SendEmail() && ArchiveResportsInDatabase() && CreateAFile();

وهذا سيعود صحيح إذا عادت جميع وظائف حقيقية، ووقف في أقرب وقت أول واحد يعود كاذبة.

لحقا تفعل ذلك الحق يجب عليك استخدام نمط الرسائل asyncronous. I انتهى لتوه من المشروع حيث فعلت ذلك باستخدام nServiceBus و MSMQ.

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

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

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