74 Stimmen

Entwurfsmuster für die Wiederholung einer fehlgeschlagenen Logik?

Ich schreibe einige Reconnect-Logik, um regelmäßig zu versuchen, eine Verbindung zu einem entfernten Endpunkt herzustellen, der ausgefallen ist. Im Wesentlichen sieht der Code wie folgt aus:

public void establishConnection() {
    try {
        this.connection = newConnection();
    } catch (IOException e) {
        // connection failed, try again.
        try { Thread.sleep(1000); } catch (InterruptedException e) {};

        establishConnection();
    }
}

Ich habe dieses allgemeine Problem schon oft mit ähnlichem Code gelöst, aber ich bin mit dem Ergebnis weitgehend unzufrieden. Gibt es ein Entwurfsmuster für die Lösung dieses Problems?

33voto

JB Nizet Punkte 654813

Schamlose Werbung: Ich habe einige Klassen implementiert, um die Wiederholung von Operationen zu ermöglichen. Die Bibliothek ist noch nicht verfügbar, aber Sie können auf github verzweigen . Und eine Gabel existe.

Es ermöglicht den Aufbau eines Retryers mit verschiedenen flexiblen Strategien. Zum Beispiel:

Retryer retryer = 
    RetryerBuilder.newBuilder()
                  .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECOND))
                  .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                  .retryIfExceptionOfType(IOException.class)
                  .build();

Und Sie können dann einen Callable (oder mehrere) mit dem Retryer ausführen:

retryer.call(new Callable<Void>() {
    public Void call() throws IOException {
        connection = newConnection();
        return null;
    }
}

30voto

GalacticJello Punkte 10815

Sie könnten versuchen, die Idempotisches Wiederholungsmuster .

enter image description here

17voto

Dherik Punkte 15172

Ich mag diesen Java 8-Code von dieser Blog und Sie brauchen keine zusätzliche Bibliothek in Ihrem Klassenpfad.

Sie müssen nur eine Funktion an die Retry-Klasse übergeben.

@Slf4j
public class RetryCommand<T> {

    private int maxRetries;

    RetryCommand(int maxRetries)
    {
        this.maxRetries = maxRetries;
    }

    // Takes a function and executes it, if fails, passes the function to the retry command
    public T run(Supplier<T> function) {
        try {
            return function.get();
        } catch (Exception e) {
            log.error("FAILED - Command failed, will be retried " + maxRetries + " times.");
            return retry(function);
        }
    }

    private T retry(Supplier<T> function) throws RuntimeException {

        int retryCounter = 0;
        while (retryCounter < maxRetries) {
            try {
                return function.get();
            } catch (Exception ex) {
                retryCounter++;
                log.error("FAILED - Command failed on retry " + retryCounter + " of " + maxRetries, ex);
                if (retryCounter >= maxRetries) {
                    log.error("Max retries exceeded.");
                    break;
                }
            }
        }
        throw new RuntimeException("Command failed on all of " + maxRetries + " retries");
    }
}

Und sie zu benutzen:

new RetryCommand<>(5).run(() -> client.getThatThing(id));

13voto

Jonathan Punkte 5518

使用する Failsafe (Autor hier):

RetryPolicy retryPolicy = new RetryPolicy()
  .retryOn(IOException.class)
  .withMaxRetries(5)
  .withDelay(1, TimeUnit.SECONDS);

Failsafe.with(retryPolicy).run(() -> newConnection());

Keine Anmerkungen, keine Magie, es muss keine Spring-App sein usw. Einfach unkompliziert und einfach.

12voto

yegor256 Punkte 96888

Ich verwende AOP und Java-Annotationen. Es gibt einen vorgefertigten Mechanismus in jcabi-Aspekte (Ich bin ein Entwickler):

@RetryOnFailure(attempts = 3, delay = 1, unit = TimeUnit.SECONDS)
public void establishConnection() {
  this.connection = newConnection();
}

ps. Sie können auch versuchen RetryScalar de Kakteen .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X