Frage

Was ist der beste Weg, um ein paar Aufgaben zusammen, und wenn eine Aufgabe versagt, dann die nächsten Aufgaben nicht abgeschlossen werden sollen durchführen? Ich weiß, wenn es die Datenbank-Operationen wurden dann sollte ich Transaktionen verwendet haben, aber ich spreche über die verschiedenen Arten von Operationen wie folgt aus:

Alle Aufgaben müssen übergeben:

Sendemail ArchiveReportsInDatabase CreateAFile

Im obigen Szenario all Aufgaben müssen übergeben oder aber der gesamte Batch-Betrieb muss ein Rollback werden.

War es hilfreich?

Lösung

Ausnahmen sind für diese Art der Sache im Allgemeinen gut. Pseudo-Java / JavaScript / C ++ Code:

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);
    ...
}

Noch besser ist es, wenn Ihre Methoden werfen Ausnahmen selbst:

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

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

Ein sehr schönes Ergebnis dieses Stils ist, dass der Code nicht immer eingerückt ist, wie Sie die Aufgabe Kette nach unten zu verschieben; alle Methodenaufrufe bleiben auf dem gleichen Einrückungsebene. Zu viel Vertiefung macht den Code schwerer zu lesen.

Darüber hinaus haben Sie einen einzigen Punkt im Code für die Fehlerbehandlung, Protokollierung, Rollback etc.

Andere Tipps

Rollbacks sind hart - AFAIK, gibt es wirklich nur zwei Möglichkeiten, um darüber zu gehen. Entweder ein 2-Phasen-Commit-Protokoll oder Kompensationstransaktionen . Sie haben wirklich einen Weg finden, Ihre Aufgaben in einer dieser Moden zu strukturieren.

In der Regel ist die bessere Idee ist harte Arbeit und den Einsatz Technologien Vorteil anderer Leute zu nehmen, die bereits 2PC oder Kompensation eingebaut. Das ist ein Grund, dass RDBMS so beliebt sind.

So

sind die Besonderheiten Aufgabe abhängig ... aber das Muster ist ziemlich einfach:

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;
   }
}

Natürlich für Ihre spezifische Aufgaben - können Sie im Glück sein.

  • Offensichtlich kann die RDBMS Arbeit bereits in einer Transaktion eingewickelt werden.
  • Wenn Sie auf Vista oder Server 2008 sind, dann erhalten Sie Transactional NTFS zur Deckung Ihr Szenario Create.
  • E-Mail ist ein bisschen schwieriger - ich weiß nicht, irgend 2PC oder Compensators um ihn herum (ich nur ein wenig überrascht sein würde, wenn jemand darauf hingewiesen, dass Exchange ein, obwohl hat), so würde ich wahrscheinlich einen href verwenden <= „http://en.wikipedia.org/wiki/Microsoft_Message_Queuing“ rel = „nofollow noreferrer“> MSMQ eine Benachrichtigung zu schreiben und lassen ein Teilnehmer es abholen und es schließlich eine E-Mail. Zu diesem Zeitpunkt Ihrer Transaktion wirklich deckt Senden Sie einfach die Nachricht an die Warteschlange, aber das ist wahrscheinlich gut genug.

Alle diese Faktoren können in einem System.Transactions Transaktion , so sollten Sie in ziemlich guter Form sein.

in C #

Rückkehr Sendemail () && ArchiveResportsInDatabase () && CreateAFile ();

Eine andere Idee:

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

Ein paar Vorschläge:

In einem verteilten Szenario eine Art Zwei-Phasen-Commit-Protokoll benötigt werden. Im Grunde sagen Sie allen Teilnehmern eine Nachricht senden „X zu tun vorbereiten“. Jeder Teilnehmer muss dann eine Antwort sagen senden „OK, ich garantiere ich X tun kann“ oder „Nein, kann es nicht tun.“ Wenn alle Teilnehmer garantieren sie abschließen können, dann die Nachricht sie es tun zu erzählen senden. Die „Garantien“ können so streng sein wie nötig.

Ein weiterer Ansatz ist eine Art von Undo-Mechanismus für jede Operation zu schaffen, dann Logik wie folgt aus:

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

(Sie würden Ihren Code nicht wollen so aussehen,. Dies ist nur ein Beispiel dafür, wie die Logik fließen soll)

Wenn Sie die Sprache erlaubt es, das ist sehr ordentlich:

  1. Legen Sie Ihre Aufgaben in einer Reihe von Codeblöcken oder Funktionszeiger.
  2. Iterate über das Array.
  3. Break, wenn jeder Block kehrt Fehler.

Sie erwähnte nicht, was Programmiersprache / Umgebung Sie verwenden. Wenn es das .NET Framework ist, möchten Sie vielleicht einen Blick nehmen unter diesem Artikel . Es beschreibt die Concurrency und Control Runtime von Microsoft Robotics Studio, mit dem Sie alle Arten von Regeln auf einer Reihe von (asynchron) Ereignissen anwenden kann: zum Beispiel, können Sie für eine beliebige Anzahl von ihnen warten zu vervollständigen, kündigen, wenn ein Ereignis fehlschlägt, und etc. Es Dinge in mehreren Threads ausgeführt werden können, so dass Sie eine sehr leistungsfähige Methode zu tun, Sachen bekommen.

Sie nicht angeben, Ihre Umgebung. In Unix-Shell-Skripten, der && Operator genau dies tut.

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

SendEmail && ArchiveReportsInDatabase && CreateAFile

Wenn Sie eine Sprache verwenden, die Art Schlussauswertung (Java und C # tun), können Sie einfach tun:

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

Dies wird return true, wenn alle Funktionen true zurück, und sobald die erste return false stoppen.

Um wirklich machen Sie es richtig, sollten Sie ein asynchrones Messaging-Muster verwenden. Ich habe gerade ein Projekt, wo ich tat dies mit nServiceBus und MSMQ.

Grundsätzlich jeder Schritt geschieht, indem eine Nachricht an eine Warteschlange zu senden. Wenn nServiceBus wartenden Nachrichten in der Warteschlange findet nennt es Ihre Methode Verarbeitung zu diesem Nachrichtentyp entspricht. Auf diese Weise ist jeder einzelne Schritt unabhängig failable und wiederholbar. Wenn ein Schritt vor der Nachricht in einer Fehlerwarteschlange endet nicht so können Sie diese leicht wiederholen später.

Diese reine Code Lösungen vorgeschlagen werden, sind nicht so robust, da, wenn ein Schritt fehlschlägt Sie keine Möglichkeit, nur noch einen Schritt in der Zukunft wiederholen würde und man müßte Rollbacks Code implementieren, die nicht einmal möglich ist, in einige Fälle.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top