[JAVA] Implémenter le traitement déclaratif des tentatives à l'aide de Spring Retry

Aperçu

--Écrire un exemple de programme et vérifier le comportement de Spring Boot + Spring Retry --Spring Retry élimine le besoin d'écrire du code pour déterminer et contrôler les tentatives de traitement

Liste du code source de l'exemple de programme

├── build.gradle
├── settings.gradle
└── src
    └── main
        └── java
            └── info
                └── maigo
                    └── lab
                        └── sample
                            └── springretry
                                ├── Application.java
                                ├── HogeController.java
                                ├── HogeService.java
                                ├── KotsuException.java
                                ├── PonException.java
                                ├── PonKotsuRepository.java
                                └── PonKotsuRetryListener.java

Exemple de présentation du programme

--Renvoie un message en JSON en réponse à une requête HTTP. --PonKotsuRepository est un dépôt pompeux. Il renvoie un message avec une certaine probabilité, mais la plupart échouent. --HogeService appellera à nouveau PonKotsuRepository et réessayera si PonKotsuRepository échoue.

Environnement de vérification de fonctionnement

Fichier de construction Gradle

build.gradle

Ajoutez implémentation 'org.springframework.retry: spring-retry: 1.2.4.RELEASE' aux dépendances pour utiliser Spring Retry. Ajoutez également runtime'org.springframework.boot: spring-boot-starter-aop 'car il nécessite la classe AOP au moment de l'exécution. Vous trouverez comment spécifier les bibliothèques dépendantes dans GitHub \ -spring \ -projects / spring \ -retry.


plugins {

  // The Java Plugin
  // https://docs.gradle.org/current/userguide/java_plugin.html
  id 'java'

  // Gradle - Plugin: org.springframework.boot
  // https://plugins.gradle.org/plugin/org.springframework.boot
  id 'org.springframework.boot' version '2.2.0.M4'

  // Gradle - Plugin: io.spring.dependency-management
  /// https://plugins.gradle.org/plugin/io.spring.dependency-management
  id 'io.spring.dependency-management' version '1.0.8.RELEASE'  
}

group = 'info.maigo.lab'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 11

repositories {
  mavenCentral()
  maven { url 'https://repo.spring.io/snapshot' }
  maven { url 'https://repo.spring.io/milestone' }
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-web:2.2.0.M4'
  implementation 'org.springframework.retry:spring-retry:1.2.4.RELEASE'
  runtime 'org.springframework.boot:spring-boot-starter-aop'
}

settings.gradle


pluginManagement {
  repositories {
    maven { url 'https://repo.spring.io/snapshot' }
    maven { url 'https://repo.spring.io/milestone' }
    gradlePluginPortal()
  }
  resolutionStrategy {
    eachPlugin {
      if (requested.id.id == 'org.springframework.boot') {
        useModule("org.springframework.boot:spring-boot-gradle-plugin:${requested.version}")
      }
    }
  }
}

rootProject.name = 'sample.springretry'

Construire et démarrer le serveur

Générez un fichier JAR avec la tâche de construction dans le plug-in Java de Gradle.

$ gradle build

BUILD SUCCESSFUL in 3s
3 actionable tasks: 3 executed

Exécutez le fichier jar généré avec la commande java. Cela démarrera le serveur Web avec Spring Boot.

$ java -jar build/libs/sample.springretry-0.0.1-SNAPSHOT.jar

Code source

Application.java

Classe d'entrée de démarrage par Spring Boot. L'annotation @EnableRetry est spécifiée pour utiliser Spring Retry.

package info.maigo.lab.sample.springretry;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;

@SpringBootApplication
@EnableRetry
public class Application {

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

}

HogeController.java

Classe de contrôleur qui reçoit une requête HTTP et renvoie une réponse. Lorsqu'une exception se produit, le résultat du traitement par la méthode avec l'annotation @ExceptionHandler est renvoyé.

package info.maigo.lab.sample.springretry;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HogeController {

    @Autowired
    private HogeService hogeService;

    @RequestMapping("/{message}")
    public Map<String, Object> index(@PathVariable("message") String message) {
        return hogeService.send(message);
    }

    @ExceptionHandler(Exception.class)
    public Map<String, Object> handleException(HttpServletRequest req, Exception e) {
        return new HashMap<String, Object>() {
            {
                put("handleException", e.getMessage());
            }
        };
    }
}

PonKotsuRepository.java

Une classe Repository sournoise. Soulevez PonException ou KotsuException avec une certaine probabilité.

package info.maigo.lab.sample.springretry;

import java.util.Random;
import org.springframework.stereotype.Repository;

/**
 *Un référentiel pompeux.
 */
@Repository
public class PonKotsuRepository {

    private static final Random r = new Random(System.currentTimeMillis());

    /**
     *Espérons qu'il renvoie un message, mais échoue presque.
     * @param message
     * @message de retour
     * @lance PonException PonException
     * @jette une exception pour les astuces KotsuException
     */
    public String send(String message) {
        int i = r.nextInt(5);
        if (i < 2) {
            System.out.println("PonKotsuRepository: Throws PonException.");
            throw new PonException();
        } else if (i < 4) {
            System.out.println("PonKotsuRepository: Throws KotsuException.");
            throw new KotsuException();
        } else {
            System.out.println("PonKotsuRepository: Returns a message.");
            return "1/Message que 3 ne transmet pas: " + message;
        }
    }
}

HogeService.java

Une classe Service qui utilise une classe Repository sournoise. L'annotation @Retryable est utilisée pour spécifier le nombre de tentatives et le temps d'attente avant les tentatives. La méthode avec l'annotation @Recover est appelée lorsqu'une exception se produit même après avoir essayé le nombre de fois spécifié. Cette méthode prend l'exception qui s'est produite lors de la dernière tentative comme premier argument et prend les arguments de la méthode avec l'annotation @Retryable après le deuxième argument. Cette fois, nous avons préparé deux méthodes qui spécifient l'annotation @Recover. L'un est utilisé lorsqu'une PonException se produit et renvoie le résultat sous forme de chaîne. L'autre est utilisé lorsqu'une KotsuException se produit et lève une RuntimeException. Les informations de l'annotation utilisée dans Spring Retry sont Spring \ -Retry \ -Simple and essential code-free retry processing framework et [org \ .springframework \ .retry \ .annotation \ (Spring Retry 1 \ .2 \ .4 \ .RELEASE API )](https://static.javadoc.io/org.springframework.retry/spring-retry/1.2. 4.RELEASE / index.html? Org / springframework / retry / annotation / package-summary.html) sera utile.

package info.maigo.lab.sample.springretry;

import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.retry.annotation.Recover;

@Service
public class HogeService {

    @Autowired
    private PonKotsuRepository repository;

    @Retryable(value = {PonException.class, KotsuException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
    public Map<String, Object> send(String message) {
        String result = repository.send(message);
        return new HashMap<String, Object>() {
            {
                put("result", result);
            }
        };
    }

    @Recover
    public Map<String, Object> recoverSend(PonException e, String message) {
        System.out.println("recoverSend: PonException");
        return new HashMap<String, Object>() {
            {
                put("error", "La dernière chose qui s'est produite était PonException");
            }
        };
    }

    @Recover
    public Map<String, Object> recoverSend(KotsuException e, String message) {
        System.out.println("recoverSend: KotsuException");
        throw new RuntimeException("La dernière chose qui s'est produite était KotsuException");
    }
}

PonException.java

Exception Pong. Il hérite simplement de RuntimeException.

package info.maigo.lab.sample.springretry;

public class PonException extends RuntimeException {
}

KotsuException.java

Exception pour les pourboires. Il hérite simplement de RuntimeException.

package info.maigo.lab.sample.springretry;

public class KotsuException extends RuntimeException {
}

PonKotsuRetryListener.java

Une classe qui implémente l'interface RetryListener. Il se compose d'un rappel qui est appelé pendant le processus de nouvelle tentative. Dans cette implémentation, l'état du traitement des nouvelles tentatives est sorti sur la sortie standard.

package info.maigo.lab.sample.springretry;

import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.listener.RetryListenerSupport;
import org.springframework.stereotype.Component;

@Component
public class PonKotsuRetryListener extends RetryListenerSupport {

    @Override
    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        System.out.println("PonKotsuRetryListener#close: " + getThrowableString(throwable));
        super.close(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        System.out.println("PonKotsuRetryListener#onError: " + getThrowableString(throwable));
        super.onError(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
        System.out.println("PonKotsuRetryListener#open");
        return super.open(context, callback);
    }

    private static String getThrowableString(Throwable throwable) {
        return throwable == null ? "null" : throwable.getClass().getSimpleName();
    }
}

Exemple d'exécution

Exemple d'exécution 1

JSON est généré lors de l'accès au serveur démarré par curl.

$ curl http://localhost:8080/hello
{"result":"1/Message que 3 ne transmet pas: hello"}

Affichez la sortie standard côté serveur. Le message peut être renvoyé normalement en réessayant après que l'exception Pon s'est produite.

PonKotsuRetryListener#open
PonKotsuRepository: Throws PonException.
PonKotsuRetryListener#onError: PonException
PonKotsuRepository: Returns a message.
PonKotsuRetryListener#close: null

Exemple d'exécution 2

$ curl http://localhost:8080/foo
{"error":"La dernière chose qui s'est produite était PonException"}

Affichez la sortie standard côté serveur. Il se produit dans l'ordre exception Pon → exception Kotsu → exception Pon. Pour la dernière exception Pon, PonKotsuRepository # recoverSend renvoie le résultat.

PonKotsuRetryListener#open
PonKotsuRepository: Throws PonException.
PonKotsuRetryListener#onError: PonException
PonKotsuRepository: Throws KotsuException.
PonKotsuRetryListener#onError: KotsuException
PonKotsuRepository: Throws PonException.
PonKotsuRetryListener#onError: PonException
recoverSend: PonException
PonKotsuRetryListener#close: PonException

Exemple d'exécution 3

$ curl http://localhost:8080/bar
{"handleException":"La dernière chose qui s'est produite était KotsuException"}

Affichez la sortie standard côté serveur. Il se produit dans l'ordre exception Pon → exception Kotsu → exception Kotsu. Pour la dernière exception Kotsu, HogeController # handleException renvoie le résultat.

PonKotsuRetryListener#open
PonKotsuRepository: Throws PonException.
PonKotsuRetryListener#onError: PonException
PonKotsuRepository: Throws KotsuException.
PonKotsuRetryListener#onError: KotsuException
PonKotsuRepository: Throws KotsuException.
PonKotsuRetryListener#onError: KotsuException
recoverSend: KotsuException
PonKotsuRetryListener#close: KotsuException

Matériel de référence

Recommended Posts

Implémenter le traitement déclaratif des tentatives à l'aide de Spring Retry
Traitement asynchrone avec Spring Boot en utilisant @Async
Dépannage de Spring Retry
Essayez d'utiliser Spring JDBC
Exécution du traitement initial à l'aide de Spring Boot Command Line Runner
Implémenter GraphQL avec Spring Boot
Spring avec Kotorin --6 Traitement asynchrone
Traitement des données avec Apache Flink
Implémenter la fonction de catégorie en utilisant l'ancêtre
Utilisation de Mapper avec Java (Spring)
Essayez d'utiliser Spring Boot Security
[Swift] Traitement asynchrone à l'aide de PromiseKit
[Traitement] Essayez d'utiliser GT Force.
Traitement de la sortie CSV avec Super-CSV