Gelegentlich muss ich eine Operation mehrmals wiederholen, bevor ich aufgebe. Mein Code lautet wie folgt:
int retries = 3;
while(true) {
try {
DoSomething();
break; // success!
} catch {
if(--retries == 0) throw;
else Thread.Sleep(1000);
}
}
Ich möchte dies in einer allgemeinen Wiederholungsfunktion wie folgt umschreiben:
TryThreeTimes(DoSomething);
Ist es in C # möglich? Was wäre der Code für die TryThreeTimes()
Methode?
Antworten:
Pauschal-Catch-Anweisungen, die denselben Aufruf einfach wiederholen, können gefährlich sein, wenn sie als allgemeiner Mechanismus zur Behandlung von Ausnahmen verwendet werden. Trotzdem ist hier ein Lambda-basierter Wiederholungs-Wrapper, den Sie mit jeder Methode verwenden können. Ich habe mich dafür entschieden, die Anzahl der Wiederholungsversuche und das Zeitlimit für Wiederholungsversuche als Parameter für etwas mehr Flexibilität zu berücksichtigen:
Mit dieser Dienstprogrammmethode können Sie jetzt eine Wiederholungslogik ausführen:
oder:
oder:
Oder Sie könnten sogar eine
async
Überlastung machen.quelle
Policy.Handle<DivideByZeroException>().WaitAndRetry(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3) });
Sie sollten Polly versuchen . Es handelt sich um eine von mir geschriebene .NET-Bibliothek, mit der Entwickler Richtlinien für die vorübergehende Ausnahmebehandlung wie "Wiederholen", "Für immer wiederholen", "Warten und erneut versuchen" oder "Leistungsschalter" fließend ausdrücken können.
Beispiel
quelle
Dies ist möglicherweise eine schlechte Idee. Erstens ist es ein Symbol für die Maxime "Die Definition von Wahnsinn macht zweimal dasselbe und erwartet jedes Mal andere Ergebnisse". Zweitens passt dieses Codierungsmuster nicht gut zu sich selbst. Zum Beispiel:
Angenommen, Ihre Netzwerkhardwareschicht sendet ein Paket bei einem Fehler dreimal erneut und wartet beispielsweise eine Sekunde zwischen den Fehlern.
Angenommen, die Softwareschicht sendet eine Benachrichtigung über einen Fehler dreimal bei einem Paketfehler erneut.
Angenommen, die Benachrichtigungsschicht reaktiviert die Benachrichtigung dreimal bei einem Fehler bei der Zustellung der Benachrichtigung.
Angenommen, die Fehlerberichtsschicht reaktiviert die Benachrichtigungsschicht dreimal bei einem Benachrichtigungsfehler.
Angenommen, der Webserver reaktiviert die Fehlerberichterstattung dreimal bei einem Fehler.
Angenommen, der Webclient sendet die Anforderung dreimal erneut, nachdem er einen Fehler vom Server erhalten hat.
Angenommen, die Leitung auf dem Netzwerk-Switch, die die Benachrichtigung an den Administrator weiterleiten soll, ist nicht angeschlossen. Wann erhält der Benutzer des Webclients endlich seine Fehlermeldung? Ich schaffe es ungefähr zwölf Minuten später.
Damit Sie nicht glauben, dass dies nur ein dummes Beispiel ist: Wir haben diesen Fehler im Kundencode gesehen, obwohl er weitaus schlimmer ist, als ich hier beschrieben habe. In dem bestimmten Kundencode betrug die Lücke zwischen dem Auftreten des Fehlerzustands und der endgültigen Meldung an den Benutzer mehrere Wochen, da so viele Ebenen automatisch mit Wartezeiten erneut versucht wurden. Stellen Sie sich vor, was passieren würde, wenn zehn statt drei Wiederholungen durchgeführt würden .
Normalerweise ist es das Richtige, eine Fehlerbedingung sofort zu melden und den Benutzer entscheiden zu lassen, was zu tun ist. Wenn der Benutzer eine Richtlinie für automatische Wiederholungsversuche erstellen möchte, lassen Sie ihn diese Richtlinie auf der entsprechenden Ebene in der Software-Abstraktion erstellen.
quelle
Dann würden Sie anrufen:
...oder alternativ...
Eine flexiblere Option:
Zu verwenden als:
Eine modernere Version mit Unterstützung für async / await:
Zu verwenden als:
quelle
--retryCount <= 0
weil dies für immer so bleibt, wenn Sie Wiederholungsversuche deaktivieren möchten, indem Sie es auf 0 setzen. Technisch gesehen ist der BegriffretryCount
kein wirklich guter Name, da er nicht wiederholt wird, wenn Sie ihn auf 1 setzen es zutryCount
oder setzen die - hinter.Thread.Sleep
. Alternativen sind die Verwendung von Timern oder heutzutage eher dieasync
Wiederholung vonTask.Delay
.returns true
?Func<bool>
Der Anwendungsblock für die vorübergehende Fehlerbehandlung bietet eine erweiterbare Sammlung von Wiederholungsstrategien, darunter:
Es enthält auch eine Sammlung von Fehlererkennungsstrategien für Cloud-basierte Dienste.
Weitere Informationen finden Sie in diesem Kapitel des Entwicklerhandbuchs.
Erhältlich über NuGet (Suche nach ' Topas ').
quelle
Funktionen berücksichtigen und Meldungen wiederholen
quelle
max retries
?Sie können auch den Ausnahmetyp hinzufügen, für den Sie es erneut versuchen möchten. Ist dies beispielsweise eine Timeout-Ausnahme, die Sie erneut versuchen möchten? Eine Datenbankausnahme?
Sie können auch feststellen, dass alle anderen Beispiele ein ähnliches Problem beim Testen auf Wiederholungsversuche == 0 haben und entweder unendlich wiederholen oder Ausnahmen nicht auslösen, wenn ein negativer Wert angegeben wird. Auch Sleep (-1000) schlägt in den obigen Fangblöcken fehl. Kommt darauf an, wie "albern" man die Leute erwartet, aber defensives Programmieren tut nie weh.
quelle
Ich bin ein Fan von Rekursions- und Erweiterungsmethoden. Hier sind meine zwei Cent:
quelle
Aufbauend auf der vorherigen Arbeit habe ich darüber nachgedacht, die Wiederholungslogik auf drei Arten zu verbessern:
Machen Sie es zu einer
Action
ErweiterungsmethodeDie Methode kann dann wie folgt aufgerufen werden (natürlich können auch anonyme Methoden verwendet werden):
quelle
Halten Sie es einfach mit C # 6.0
quelle
Verwenden Sie Polly
https://github.com/App-vNext/Polly-Samples
Hier ist ein Wiederholungs-Generikum, das ich mit Polly verwende
Verwenden Sie es so
quelle
Die Antwort von LBushkin wurde auf die neueste Weise umgesetzt:
und um es zu benutzen:
wohingegen die Funktion
[TaskFunction]
entwederTask<T>
oder nur sein kannTask
.quelle
Ich würde dies implementieren:
Ich würde Ausnahmen nicht so verwenden, wie sie in den anderen Beispielen verwendet werden. Es scheint mir, dass wenn wir die Möglichkeit erwarten, dass eine Methode nicht erfolgreich sein wird, ihr Fehler keine Ausnahme ist. Die Methode, die ich aufrufe, sollte also true zurückgeben, wenn sie erfolgreich war, und false, wenn sie fehlgeschlagen ist.
Warum ist es ein
Func<bool, bool>
und nicht nur einFunc<bool>
? Also das, wenn ich will eine Methode bei einem Fehler eine Ausnahme auslösen kann, kann ich sie darüber informieren, dass dies der letzte Versuch ist.Also könnte ich es mit Code verwenden wie:
oder
Wenn sich die Übergabe eines Parameters, den die Methode nicht verwendet, als umständlich herausstellt, ist es trivial, eine Überladung zu implementieren, für
Retry
die nur ein erforderlich istFunc<bool>
.quelle
StopIteration
Ausnahme durch Python ), und es gibt einen Grund.TryDo
Methodenmuster ist eine rutschige Steigung. Bevor Sie es wissen, besteht Ihr gesamter Aufrufstapel ausTryDo
Methoden. Ausnahmen wurden erfunden, um ein solches Durcheinander zu vermeiden.Exponentielles Backoff ist eine gute Wiederholungsstrategie, als es einfach x-mal zu versuchen. Sie können eine Bibliothek wie Polly verwenden, um sie zu implementieren.
quelle
Verwenden Sie Folgendes, wenn Sie beide Optionen haben möchten, um eine Ausnahme erneut zu versuchen oder den Ausnahmetyp explizit festzulegen.
quelle
Ich brauchte eine Methode, die das Abbrechen unterstützt. Während ich dabei war, habe ich Unterstützung für die Rückgabe von Zwischenfehlern hinzugefügt.
Sie können diese
Retry
Funktion wie folgt verwenden. Versuchen Sie es dreimal mit einer Verzögerung von 10 Sekunden, jedoch ohne Abbruch.Oder versuchen Sie es ewig alle fünf Sekunden, sofern nicht abgebrochen.
Wie Sie
Retry
sich vorstellen können, habe ich in meinem Quellcode die Funktion überladen , um die unterschiedlichen Delgate-Typen zu unterstützen, die ich verwenden möchte.quelle
Diese Methode ermöglicht Wiederholungsversuche für bestimmte Ausnahmetypen (löst andere sofort aus).
Anwendungsbeispiel:
quelle
Update nach 6 Jahren: Jetzt halte ich den folgenden Ansatz für ziemlich schlecht. Um eine Wiederholungslogik zu erstellen, sollten Sie eine Bibliothek wie Polly verwenden.
Meine
async
Implementierung der Wiederholungsmethode:Wichtige Punkte: Ich habe
.ConfigureAwait(false);
undFunc<dynamic>
stattdessen verwendetFunc<T>
quelle
Task.Delay
wird ohne Grund aufgerufen.Oder wie wäre es ein bisschen ordentlicher ...
Ich bin der Meinung, dass das Auslösen von Ausnahmen im Allgemeinen als Mechanismus vermieden werden sollte, es sei denn, Sie überschreiten sie zwischen Grenzen (z. B. beim Aufbau einer Bibliothek, die andere Personen verwenden können). Warum nicht einfach den
DoSomething()
Befehl zurückgeben lassen,true
wenn er erfolgreich war undfalse
sonst?BEARBEITEN: Und dies kann in eine Funktion eingekapselt werden, wie es auch andere vorgeschlagen haben. Das einzige Problem ist, wenn Sie die
DoSomething()
Funktion nicht selbst schreibenquelle
Ich musste einen Parameter an meine Methode übergeben, um es erneut zu versuchen, und einen Ergebniswert haben. Also brauche ich einen Ausdruck. Ich baue diese Klasse auf, die die Arbeit macht (sie ist inspiriert von der des LBushkin). Du kannst sie so verwenden:
ps. Die LBushkin-Lösung führt einen weiteren Versuch durch = D.
quelle
Ich würde der akzeptierten Antwort den folgenden Code hinzufügen
Grundsätzlich macht der obige Code die
Retry
Klasse generisch, sodass Sie den Typ der Ausnahme übergeben können, die Sie für einen erneuten Versuch abfangen möchten.Verwenden Sie es jetzt fast genauso, geben Sie jedoch den Ausnahmetyp an
quelle
action
nicht geworfen wird,Do
kehrtbreak
diefor
Schleife zurück.Thread.Sleep
wird ohne Grund aufgerufen.Ich weiß, dass diese Antwort sehr alt ist, aber ich wollte dies nur kommentieren, weil ich bei der Verwendung dieser Probleme auf Probleme gestoßen bin, während ich eine Aussage mit Zählern mache.
Im Laufe der Jahre habe ich mich für einen besseren Ansatz entschieden, denke ich. Das heißt, eine Art Ereignisaggregation wie eine reaktive Erweiterung "Subject" oder dergleichen zu verwenden. Wenn ein Versuch fehlschlägt, veröffentlichen Sie einfach ein Ereignis, das besagt, dass der Versuch fehlgeschlagen ist, und lassen die Aggregatorfunktion das Ereignis neu planen. Dies ermöglicht Ihnen viel mehr Kontrolle über den Wiederholungsversuch, ohne den Anruf selbst mit einer Reihe von Wiederholungsschleifen zu verschmutzen und was nicht. Sie binden auch keinen einzelnen Thread mit einem Bündel von Thread-Schlafplätzen zusammen.
quelle
Machen Sie es einfach in C #, Java oder anderen Sprachen:
und verwenden Sie es in Ihrem Code sehr einfach:
oder Sie können es in rekursiven Methoden verwenden:
quelle
quelle
Request.BadRequest
?Wiederholungs-Helfer: Eine generische Java-Implementierung, die sowohl wiederkehrbare als auch nichtige Wiederholungsversuche enthält.
Verwendungszweck:
quelle
Hier ist eine
async
/await
Version, die Ausnahmen aggregiert und die Stornierung unterstützt.quelle
quelle
throw;
einzige Möglichkeit ist, die Endlosschleife zu beenden, implementiert diese Methode tatsächlich "Versuch, bis sie N-mal fehlschlägt" und nicht den gewünschten "Versuch bis zuN
Zeiten, bis sie erfolgreich ist". Sie benötigen einbreak;
oderreturn;
nach dem Anruf,ThingToTryDelegate();
sonst wird es kontinuierlich aufgerufen, wenn es nie fehlschlägt. Dies wird auch nicht kompiliert, da der erste Parameter vonTryNTimes
keinen Namen hat. -1.Ich habe eine kleine Klasse geschrieben, die auf den hier veröffentlichten Antworten basiert. Hoffentlich hilft es jemandem: https://github.com/natenho/resiliency
quelle
Ich habe eine asynchrone Version der akzeptierten Antwort wie folgt implementiert - und es scheint gut zu funktionieren - irgendwelche Kommentare?
Und nennen Sie es einfach so:
quelle
Thread.Sleep
? Durch das Blockieren eines Threads werden die Vorteile der Asynchronität zunichte gemacht. Ich bin mir auch ziemlich sicher, dass dieTask DoAsync()
Version ein Argument vom Typ akzeptieren sollteFunc<Task>
.