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.
- HTTP-Aufruf an einen REST-Endpunkt
- Senden oder Abrufen von Nachrichten von SQS
- Remote-Prozeduraufruf oder ein Webdienst
- 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.
- Mit
@Retryable
Anmerkung - 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:
- maxAttempts – 3 ist die Standardanzahl von Versuchen für Wiederholungen.
- waitDuration – eine feste Wartezeit zwischen jedem Wiederholungsversuch.
- intervalFunction – eine Funktion zum Ändern des Warteintervalls nach einem Fehler.
- retryOnResultPredicate – konfiguriert ein Prädikat, das auswertet, ob ein Ergebnis wiederholt werden soll.
- retryExceptions – Konfiguriert eine Liste von Throwable-Klassen, die für Wiederholungsversuche verwendet werden
- ignoreExceptions – Konfiguriert eine Liste von Throwable-Klassen, die ignoriert werden
- 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.