Umgang mit Fehlern und Rückmeldungen bei der Durchführung von Massenvorgängen in einer mehrschichtigen Architektur

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

Frage

Nehmen wir an, Sie verfügen über eine Geschäftslogikmethode, die eine Operation für mehrere Objekte ausführen kann.Vielleicht möchten Sie einmal für jede aus einer Liste ausgewählte Person einen Webdienst zur Lotterienummernauswahl anrufen.In Java könnte der Code etwa so aussehen:

Set<Person> selectedPeople = ... // fetch list of people
for ( Person person : selectedPeople ) {
    String lotteryNumber = callLotteryNumberWebService( person );
    // ...
}

Beachten Sie, dass der Webdienst für Lotterienummern Nebenwirkungen haben kann, z. B. die Aufzeichnung, dass die Person eine Lotterienummer angefordert hat (und möglicherweise ihr Konto belastet). Selbst wenn der Webdienstaufruf für eine Person fehlschlägt, kann er für andere erfolgreich sein.Diese Informationen (die Lottozahlen) müssen an eine höhere Ebene (die Ansicht) zurückgemeldet werden.

Wenn in diesem Fall ein einzelner Vorgang stattgefunden hätte, könnte die Geschäftslogikmethode einen einzelnen Wert (z. B. die Lotterienummer) zurückgeben oder eine Ausnahme mit allen Details des Fehlers auslösen.Bei Massenoperationen wäre es jedoch möglich, dass einige der Operationen erfolgreich sind und einige fehlschlagen.

Dies scheint ein Problem zu sein, das in vielen Anwendungen auftreten würde, und es sollte eine saubere Möglichkeit geben, damit umzugehen.Was ist also der beste Weg, diese Art von Informationen von der Geschäftslogikschicht an eine andere Schicht in einer Anwendung (z. B. eine Ansicht) zurückzugeben, vorzugsweise auf generische Weise, die für verschiedene Arten von Daten und Vorgängen wiederverwendet werden kann?

War es hilfreich?

Lösung

Wenn ich das verstehe, haben Sie eine Situation, in der einige Anfragen erfolgreich sein können und andere fehlschlagen können.Ich bin mir nicht sicher, wo der Fehler zurückkommen soll, aber Sie könnten eine der folgenden Ursachen haben (oder eine Variante oder eine Kombination):

  • Eine Liste der Fehler und der betroffenen Domänenobjekte.Ein Basisdomänenobjekt oder etwas mit einer persistenten ID könnte für die Wiederverwendung nützlich sein.Z.B.eine Sammlung von Fehlern, die sich auf Domänenobjekte beziehen.
  • Sie könnten (AOP, DI) eine Art Fehlerobjekt/-meldung in das Person-Objekt einfügen.Z.B.wenn (Person.Fehler){...}
  • Sie könnten die Personensammlung in eine Nachricht mit Header, Text und Fehlerinformationen einschließen
  • Alle Ihre Domänenobjekte könnten eine Fehlersammlung enthalten, auf die über eine Schnittstelle zugegriffen werden kann.oder Person unterstützt die IHasErrors-Schnittstelle.Sie könnten dies generisch gestalten und ein Basis-Error-Objekt verwenden, das Warnungen und Validierungen und alle möglichen Dinge unterstützt.

Wenn Sie sich in einem echten mehrschichtigen (statt geschichteten) System befinden, verfügen Sie möglicherweise über eine nachrichtenbasierte Architektur, die problemlos eine Art generischen Fehler-/Warnungs-/Validierungsmechanismus integrieren könnte.Hierfür bieten sich SOA/Ajax-Systeme an.

Wenn Sie spezielle Fragen haben, gehen wir gerne etwas tiefer in die Materie ein.

Andere Tipps

Diese Frage hebt wichtige Unterschiede zwischen der geeigneten Verwendung der Ausnahmebehandlung, Transaktionen und der Idee hervor Workflow „Vergütung“ Das ist es, worauf der Fragesteller hinaus will, wenn er richtig sagt:

Dies scheint ein Problem zu sein, das in vielen Anwendungen auftreten würde, und es sollte eine saubere Möglichkeit geben, damit umzugehen.

Es handelt sich um ein häufiges Problem. Zunächst einige Hintergrundinformationen zum Transaktionsansatz, den Sie derzeit versuchen:

Datentransaktionen wurden ursprünglich nach der doppelten Buchführung modelliert – einer einfachen Buchführung Kredit und ein entsprechendes Lastschrift entweder gemeinsam aufgenommen werden mussten oder gar nicht.Je größer die Transaktionen werden, desto problematischer wird es, sie korrekt umzusetzen, und es wird schwieriger, mit einem Fehler umzugehen.Wenn Sie anfangen, die Idee einer einzelnen Transaktion über Systemgrenzen hinweg zu tragen, gehen Sie höchstwahrscheinlich falsch an die Sache heran.Dies ist möglich, erfordert jedoch komplexe Transaktionskoordinatoren mit zwangsläufig höherer Latenz.Ab einem gewissen Umfang sind Transaktionen die falsche Einstellung, und eine Vergütung macht viel mehr Sinn.

Hier kehren Sie zurück und sehen sich an, was das Unternehmen tatsächlich tut.Eine einzelne große Transaktion ist höchstwahrscheinlich nicht das, was die Geschäftsleute sehen.Normalerweise sehen sie, dass ein Schritt abgeschlossen ist, und abhängig von den späteren Ergebnissen können unterschiedliche Maßnahmen zur Kompensation erforderlich sein.Hier liegt die Idee eines Workflows und Entschädigung kommt herein. Hier finden Sie eine Einführung in diese Konzepte

Wenn Sie beispielsweise ein Buch bei Amazon bestellen, wird der Datensatz wahrscheinlich nicht „gesperrt“, während er sich in Ihrem Warenkorb befindet, und es werden nicht einmal strikte Transaktionen verwendet, um festzustellen, ob das Buch noch vorrätig ist, wenn die Bestellung bestätigt wird.Sie werden es Ihnen sowieso verkaufen und versenden, wenn sie können.Wenn es ihnen nicht gelingt, das Produkt innerhalb weniger Wochen auf Lager zu haben, werden sie Ihnen wahrscheinlich eine E-Mail senden, in der sie Ihnen mitteilen, dass sie versuchen, Ihren Bedarf zu decken, und Sie können weiter warten, bis sie es auf Lager haben, oder Sie können Ihre Bestellung stornieren.Dies wird als Kompensation bezeichnet und ist in vielen realen Geschäftsprozessen notwendig.

Endlich gibt es nichts Außergewöhnliches über irgendetwas davon.Erwarten Sie, dass dies passieren kann, und verwenden Sie den normalen Kontrollfluss.Sie sollten hier nicht die Ausnahmebehandlungsfunktionen Ihrer Sprache verwenden (Gute Regeln dafür, wann eine Ausnahme ausgelöst werden soll).Sie sollten sich auch nicht auf werkzeugspezifische (WCF?) Mechanismen verlassen, um Ausnahmen zu erkennen oder zu behandeln, die innerhalb der Dienstimplementierung auftreten.Die Mitteilung von Störungen sollte ein normaler Bestandteil Ihres Datenvertrags (Störungsverträge) sein.

Leider gibt es bei der „sauberen Art, damit umzugehen“ kein Flag, das sich auf magische Weise darum kümmert. Sie müssen das Problem weiter zerlegen und sich mit allen resultierenden Teilen befassen.Wir hoffen, dass diese Konzepte Sie mit dem verbinden, was andere Leute getan haben, als sie sich mit diesem Problem beschäftigt haben.

Zusammenfassung:

  • Ihr Problem ist über das Konzept einer Transaktion hinausgewachsen – schauen Sie sich die Workflow-Vergütung an.

Viel Glück -

Ich würde es vorziehen, eine Sammlung benutzerdefinierter Fehlerobjekte zurückzugeben, die das Objekt identifizieren, das durch den Fehler, den Fehlercode und die Beschreibung beeinflusst wird.Auf diese Weise kann versucht werden, die Fehler zu beheben oder sie dem Benutzer weiter anzuzeigen.

Ich denke, dass Sie Ausnahmen wirklich überbeanspruchen, wenn Sie in diesen Begriffen denken!

Es ist völlig in Ordnung, Werte zurückzugeben, die einen Fehler bedeuten, anstatt eine Ausnahme auszulösen.Oft ist es besser.Ausnahmen werden am besten verwendet, wenn Sie auf der Abstraktionsebene, auf der Sie sich befinden, keine Wiederherstellung durchführen können. Sie sollten sie jedoch nicht als Hauptmittel für den Kontrollfluss verwenden, da Ihre Programme sonst sehr schwer lesbar werden.

Der Webdienst gibt keine Ausnahmen zurück, sondern Rückgabecodes und Meldungen.Ich würde eine nützliche Darstellung speichern, die die zurückgegebenen Informationen darstellt, und die Liste dieser Informationen für die Ansicht oder was auch immer sie anzeigen wird, zurückgeben.

Im Idealfall sollte der Aufruf Ihres Webdienstes so erfolgen.

List<Person> selectedPeople = ... //fetch list of people
callLotteryNumberWebService(selectedPeople.ToArray );

Es ist kostspielig, für jede Person einen Webservice-Anruf zu tätigen.Auf der Serverseite müssen Sie die Liste durchlaufen und den Vorgang ausführen.Der serverseitige Code kann zwei Ausnahmen auslösen:BulkOperationFailedException – wenn ein schwerwiegender Fehler aufgrund eines Datenbankausfalls oder einer fehlenden Konfigurationsdatei vorliegt.Eine weitere Bearbeitung ist nicht möglich.BulkOperationException – enthält eine Reihe von Ausnahmen, die sich auf eine Person beziehen.Sie können eine bestimmte ID beibehalten, um eindeutig auf jedes Objekt zu verweisen.Ihr Code wird so aussehen:

List<Person> selectedPeople = ... // fetch list of people 

try{
    callLotteryNumberWebService(selectedPeople.ToArray);
}catch  (BulkOperationFailedException e) {
    SOP("Some config file missing/db down.No person records processed")
}catch(BulkOperationException e)  {
    UserDefinedExceptions us =  e.getExceptions()
    foreach(exception ein us)   {
        // read unique id to find which person object failed
    }
}

construct msg based on which personobject succeeded and which failed

Der Vorgang gilt als erfolgreich, wenn keine Ausnahmen ausgelöst werden.Sie können benutzerdefinierte Fehlercodes für Fehler verwenden, anstatt benutzerdefinierte Ausnahmen zu verwenden.Das Erstellen der BulkOperationException auf der Serverseite ist schwierig.Zweitens müssen Sie vom Server ausgelöste Fehler in „BulkOperationFailedException“ und „BulkOperationException“ kategorisieren.So hatte ich es in einem meiner Projekte gehandhabt

Ich würde es mir ansehen DTOs für diese Art von Aufgabe.Das DTO könnte auch Informationen darüber enthalten, ob die Beibehaltung erfolgreich war oder nicht, sowie andere Arten von „Metadaten“.

Ich würde wahrscheinlich eine Ergebniskarte vom Typ zurückgeben Map<Person,Future<String>> Von meinem getLotteryNumbers<Collection<Person>> Service.

Ich würde dann die Karte durchlaufen und die verwenden Future.get() um entweder die Lottozahl oder die ausgelöste Ausnahme zu erhalten.

In einigen meiner Dienste möchte ich alle Anrufe als einzelne Elementaufrufe implementieren und dann in meinem Dienst eine Logik haben, um sie zu bündeln und als Gruppe zu verarbeiten.Die Dosierung erfolgt über a LinkedBlockingQueue und ein Umfragethread.

In diesem Szenario gebe ich a zurück Future<Thing> die mit a darauf wartet, dass die Batch-Ergebnisse verfügbar sind CountdownLatch.

Werfen Sie einen Blick auf Java Concurrency in der Praxis, um zu sehen, wie diese Komponenten alle zusammenarbeiten können http://jcip.net/

Eine andere Möglichkeit, insbesondere für Systeme mit hohem Durchsatz, wäre die Verwendung eines auf Warteschlangen basierenden Designs, bei dem eine Verarbeitungseinheit Operationen an einem Objekt ausführt und das Objekt dann basierend auf den Ergebnissen in verschiedene Warteschlangen zur weiteren Verarbeitung durch andere Einheiten einstellt und dann weitergeht .Dies würde Engpässe reduzieren, die durch zusätzliche Verarbeitungsschritte entstehen würden, die z. B. erforderlich wären.Bearbeitung der Bestellung für nicht vorrätige Produkte

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