Java >> Java Tutorial >  >> Tag >> Spring

Spring Retry vs. Resilience4j Retry

In diesem Beitrag zeige ich den Vergleich der beiden Wiederholungen – Spring Retry vs. Resilience4j Retry. Normalerweise können Sie die Wiederholung bei der Implementierung mit einem Trennschalter kombinieren, um Ihre Anwendung robuster zu machen. Ich habe bereits die Leistungsschalter-Demo behandelt. Außerdem habe ich mein Buch Simplifying Spring Security with Okta Demo aktualisiert, falls Sie mehr über Spring Security erfahren möchten.

Spring Retry vs. Resilience4j Retry

Spring Retry ermöglicht es Anwendungen, einen fehlgeschlagenen Vorgang automatisch zu wiederholen. Wenn Ihr Dienst einen anderen Dienst aufruft und ein anderer Dienst aus irgendeinem Grund nicht antwortet, können Sie in den meisten Fällen Spring Retry verwenden, um denselben Vorgang zu wiederholen. Dies ist eine weitere Möglichkeit, Ihren Dienst verfügbarer zu machen.

Retry macht Ihre Anwendung robuster und weniger fehleranfällig. Sie können entweder Spring Retry für eine Methode konfigurieren, von der Sie glauben, dass sie fehlschlagen kann, oder Sie können einen RetryTemplate konfigurieren . Die einfache Konfiguration macht Spring Retry zu einer einfacheren Wahl beim Schreiben von Code.

Andererseits bietet das Resilience4j Retry-Modul eine ebenso einfachere Konfiguration – entweder durch Code oder durch Eigenschaften.

In diesem Beitrag werde ich zeigen, wie man Spring Retry- und Resilience4j Retry-Module beim Aufrufen von Methoden oder Diensten verwendet.

Wann sollte „Wiederholen“ verwendet werden?

Normalerweise sollten Sie den Wiederholungsvorgang in bestimmten Szenarien in Erwägung ziehen.

  1. HTTP-Aufruf an einen REST-Endpunkt
  2. Senden oder Abrufen von Nachrichten von SQS
  3. Remote-Prozeduraufruf oder ein Webdienst
  4. Abrufen oder Speichern von Daten aus Datenbanken

In solchen Fällen können wir entweder einen Fehler ausgeben, wenn wir die Operation nicht erfolgreich durchführen. Da die Verfügbarkeit von Anwendungen jedoch immer wichtiger wird, sind diese Fehler meist trivial und die meisten Dienste sind innerhalb weniger Millisekunden bis Sekunden wieder online.

Daher ist es sinnvoll, Wiederholung anzuwenden. Sie müssen darauf achten, dass die Operation, mit der Sie retry anwenden, idempotent sein muss. Angenommen, Ihre Anwendung hat eine Anfrage gesendet und der Zieldienst hat die Anfrage erhalten, aber zwischendurch ist etwas passiert und Ihr Zieldienst konnte nicht rechtzeitig antworten. Dann sollte der Zieldienst bei Wiederholung den Wiederholungsversuch nicht als separate oder neue Anforderung behandeln. Dadurch wird Ihr System widerstandsfähiger.

Spring Retry

In diesem Abschnitt zeige ich verschiedene Möglichkeiten zur Verwendung von Spring Retry. Zu Beginn haben wir eine einfache Spring Boot REST-Anwendung, um eine Liste von Unternehmen aus der Datenbank abzurufen. Wie üblich werde ich nicht zeigen, wie man eine Spring Boot-Anwendung erstellt.

Gradle-Abhängigkeiten

Um Spring Retry zu verwenden, benötigen wir zwei Abhängigkeiten in unserer Konfiguration.

        implementation 'org.springframework.retry:spring-retry:1.3.1'
	implementation 'org.springframework:spring-aspects:5.3.5'

EnableRetry-Anmerkung

Einmal haben wir spring-retry Abhängigkeit können wir unsere Hauptklasse mit der Anmerkung @EnableRetry annotieren wie folgt:


package com.betterjavacode.retrydemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;


@SpringBootApplication
@EnableRetry
@EnableJpaRepositories(basePackages = "com.betterjavacode.retrydemo.daos")
public class RetrydemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(RetrydemoApplication.class, args);
	}

	@Bean
	public RetryTemplate retryTemplate()
	{
		RetryTemplate retryTemplate = new RetryTemplate();

		FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
		backOffPolicy.setBackOffPeriod(100);

		SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
		simpleRetryPolicy.setMaxAttempts(2);

		retryTemplate.setRetryPolicy(simpleRetryPolicy);
		retryTemplate.setBackOffPolicy(backOffPolicy);
		return retryTemplate;
	}


}

Ich werde den Rest des Codes im weiteren Verlauf erklären, aber beachte hier einfach die Anmerkung @EnableRetry . Dadurch wird die Wiederholung in unserer Anwendung aktiviert.

REST-Controller

Wir zeigen Spring Retry auf zwei verschiedene Arten.

  1. Mit @Retryable Anmerkung
  2. Mit RetryTemplate

Unser REST-Controller holt uns eine Liste von Unternehmen, ein Unternehmen nach ID oder eine Liste von Unternehmen nach Namen. Es sieht wie folgt aus:


package com.betterjavacode.retrydemo.controllers;

import com.betterjavacode.retrydemo.dtos.CompanyDto;
import com.betterjavacode.retrydemo.service.CompanyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/v1/betterjavacode/companies")
public class CompanyController
{
    @Autowired
    CompanyService companyService;

    @GetMapping
    public ResponseEntity<List> getAllCompanies()
    {
        List companyDtos = companyService.getAllCompanies();

        if(companyDtos.isEmpty())
        {
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        }

        return new ResponseEntity<>(companyDtos, HttpStatus.OK);
    }

    @GetMapping("/{id}")
    public ResponseEntity getCompanyById(@PathVariable("id") long id)
    {
        CompanyDto companyDto = companyService.getCompany(id);
        if(companyDto == null)
        {
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        }
        return new ResponseEntity<>(companyDto, HttpStatus.OK);
    }

    @GetMapping("/")
    public ResponseEntity<List> searchCompanies(@RequestParam("name") String companyName)
    {
        List companyDtos = companyService.searchCompanyByName(companyName);
        if(companyDtos.isEmpty())
        {
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        }

        return new ResponseEntity<>(companyDtos, HttpStatus.OK);
    }
}

In unserem Controller verwenden wir einen @Service Objekt namens CompanyService . Dieses Dienstobjekt bietet uns eine Möglichkeit, unsere Methoden zum Abrufen von Unternehmensdaten zu implementieren.

Dienst mit verschiedenen Wiederholungskonfigurationen

Wir werden also sehen, wie wir die Anmerkung @Retryable verwenden können :


    @Retryable(value = SQLException.class, maxAttempts = 2, backoff = @Backoff(delay = 100))
    public List getAllCompanies()
    {
        List companies =  companyRepository.findAll();
        List companyDtos = new ArrayList<>();
        for(Company company : companies)
        {
            CompanyDto companyDto = new CompanyDto(company.getName(), company.getType(),
                    company.getCity(), company.getState(), company.getDescription());
            companyDtos.add(companyDto);
        }

        return companyDtos;
    }

Im obigen Code rufen wir eine Liste von Unternehmen ab. Wenn diese Methode das Ergebnis mit einer Ausnahme im Zusammenhang mit SQLException nicht abrufen kann , wiederholen wir den Abruf. Wir werden dies zweimal wiederholen, wie mit maxAttempts konfiguriert . Zwischen jedem Versuch gibt es eine Verzögerung von 100 Millisekunden. Wenn wir nun unsere Anwendung ausführen und diese Methode aufrufen, werden wir sehen, wie diese Wiederholung funktioniert.

Um den Fehler zu simulieren, stoppe ich den SQL-Dienst von den Windows-Diensten. Ich werde unten eine erfolgreiche Antwort und eine wiederholte Antwort zeigen:

Wie Sie im obigen Screenshot sehen können, gab es zwei Wiederholungsversuche. Bei jedem erneuten Versuch wurde dreimal versucht, eine Verbindung zum MySQL-Server herzustellen.

Was ist die Spring Boot-Wiederholungsvorlage?

In ähnlicher Weise können wir auch die Wiederholungsvorlage verwenden, die Spring-Retry anbietet. Im folgenden Code zeige ich eine Methode, die ich in CompanyService hinzugefügt habe um Unternehmensdaten für eine ID zu erhalten.


    public CompanyDto getCompany(long id)
    {
        CompanyDto companyDto = retryTemplate.execute(rt -> {
           Company company = companyRepository.findById(id).get();
           CompanyDto localCompanyDto = new CompanyDto(company.getName(), company.getType(),
                   company.getCity(),
                   company.getState(), company.getDescription());
           return localCompanyDto;
        });

        return companyDto;
    }

Diese retryTemplate-Bean ist mit simpleRetryPolicy konfiguriert mit 2 Versuchen und 100 Millisekunden Verzögerung zwischen jedem Versuch. Wenn ich jedoch versuche, diese Methode genauso auszuführen, wie ich es für @Retryable getan habe , sehen wir die folgende Ausgabe:

Wie oben erwähnt, stoppe ich lediglich meinen MySQL-Dienst von Windows-Diensten und es ermöglicht meiner Methode, ausgeführt zu werden, um es erneut zu versuchen.

Ist der Wiederholungsvorlagen-Thread sicher?

Retry Template-Klasse ist Thread-sicher. Es ermöglicht den gleichzeitigen Zugriff. Dafür kann man mehrere Operationen ausführen.

Resilience4j Wiederholen

Bei Verwendung von resilience4j-retry Bibliothek können Sie einen benutzerdefinierten globalen RetryConfig registrieren mit einem RetryRegistry Baumeister. Verwenden Sie diese Registrierung, um eine Wiederholung zu erstellen. In unserer Demo zum Abrufen von Unternehmensdaten haben wir eine neue Methode zum Abrufen von Unternehmen nach Namen hinzugefügt.

Diese Methode sieht wie folgt aus:


    public List searchCompanyByName(String name)
    {
        LOGGER.info("Search for company = {}", name);

        RetryConfig retryConfig =
                RetryConfig.custom().maxAttempts(4).waitDuration(Duration.of(2, SECONDS)).build();         

        RetryRegistry retryRegistry = RetryRegistry.of(retryConfig);

        Retry retryConfiguration = retryRegistry.retry("companySearchService", retryConfig);

        Supplier<List> companiesSupplier = () -> companyRepository.findAllByName(name);

        Supplier<List> retryingCompaniesSearch =
                Retry.decorateSupplier(retryConfiguration, companiesSupplier);

        List companyDtos = new ArrayList<>();
        List companies = retryingCompaniesSearch.get();
        LOGGER.info("Retrying..");
        for(Company company : companies)
        {
            CompanyDto companyDto = new CompanyDto(company.getName(), company.getType(),
                    company.getCity(), company.getState(), company.getDescription());
            companyDtos.add(companyDto);
        }

        return companyDtos;
    }

In der obigen Methode erstellen wir zuerst RetryConfig . Wir erstellen einen RetryRegistry und fügen Sie RetryConfig hinzu in diesem Register. Wenn wir dann unseren Anruf erstellen, um eine Liste von Unternehmen abzurufen. Wir schmücken diesen Aufruf mit retryConfiguration .

Anpassungen mit Resilience4j-Retry

RetryConfig bietet verschiedene Anpassungsmöglichkeiten:

  1. maxAttempts – 3 ist die Standardanzahl von Versuchen für Wiederholungen.
  2. waitDuration – eine feste Wartezeit zwischen jedem Wiederholungsversuch.
  3. intervalFunction – eine Funktion zum Ändern des Warteintervalls nach einem Fehler.
  4. retryOnResultPredicate – konfiguriert ein Prädikat, das auswertet, ob ein Ergebnis wiederholt werden soll.
  5. retryExceptions – Konfiguriert eine Liste von Throwable-Klassen, die für Wiederholungsversuche verwendet werden
  6. ignoreExceptions – Konfiguriert eine Liste von Throwable-Klassen, die ignoriert werden
  7. failAfterMaxRetries – Ein boolescher Wert zum Aktivieren oder Deaktivieren des Auslösens von MaxRetriesExceededException, wenn die Wiederholung die konfigurierten maxAttempts erreicht hat

Demo

Schauen wir uns nun an, was passiert, wenn wir diese Methode mit resilience4j-retry ausführen . Der folgende Screenshot zeigt die erfolgreiche Antwort, wenn der SQL-Dienst noch läuft.

Wenn ich den SQL-Dienst stoppe, sehen wir die Wiederholungsversuche 4 Mal, da wir ihn für 4 Mal konfiguriert haben.

Code

Den Code für diese Demo finden Sie in meinem Github-Repository.

Schlussfolgerung

In diesem Beitrag habe ich den Vergleich zwischen Spring Retry und Resilience4j Retry gezeigt. Wann Sie eine dieser Bibliotheken verwenden, hängt von Ihrem Szenario ab. Normalerweise funktioniert Resilience4j Retry gut, wenn Sie auch das Resilience4j-Circuit-Breaker-Modul planen. Spring Retry kann auch bei verschiedenen Konfigurationen mit RetryTemplate praktisch sein .

Wenn Ihnen dieser Beitrag gefallen hat, abonnieren Sie bitte meinen Blog hier.


Java-Tag