[JAVA] Resilience4j TimeLimiter expire les appels de méthode

Contexte

Il existe de plus en plus de scènes dans lesquelles Azure Cosmos DB, AWS S3, le service d'envoi de télécopie et le SDK qui appelle un service externe sont utilisés.

Dans le monde où nous avons vécu normalement, nous n'avons qu'à nous soucier de la vie ou de la mort du serveur de base de données que nous gérons, et la distance réseau est très proche. Tout était conforme aux attentes, mais ce n'est pas simple lors de l'utilisation de ces services externes.

Les retards du réseau ne sont pas stables et peuvent parfois se produire en quelques secondes. De nombreux services sont fournis avec des terminaux par nom d'hôte et les adresses IP changent sans préavis.

Lorsqu'un tel événement instable se produit, les threads qui appellent chaque service en tant que HTTP et le bloquent peuvent soudainement s'accumuler, provoquant finalement l'arrêt de l'ensemble du service.

En d'autres termes, pour bloquer les appels, veillez à ne pas remplir le thread HTTP principal avec un timeout dans l'appel de fonction.

Avec Ruby, c'est relativement facile à écrire.

require 'timeout'

def callAnotherWorld
    sleep 3
    return "Le résultat de l'appel d'un service externe"
end

begin
  result = Timeout.timeout(1) do #Timeout dans 1 seconde
    callAnotherWorld()
  end
rescue Timeout::Error
  result = ""
end

p result                         #Vide car il expire

Le code est presque le même en Java.

private final ExecutorService threadPool = Executors.newCachedThreadPool();

private String callAnotherWorld() {
    Future<String> f = threadPool.submit(() -> innerCallAnotherWorld());

    while (true) {
        try {
            f.get(1, TimeUnit.SECONDS);  //Timeout dans 1 seconde
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException retry) {
        }
    }
}

private String innerCallAnotherWorld() {
    return "Le résultat de l'appel d'un service externe";
}

Dans Ruby et Java, la méthode est exécutée dans un thread d'arrière-plan et le thread appelant attend sa fin.

Dans le cas de Java, l'existence du pool de threads et l'existence de Future ont été vues, ce qui me déçoit un peu.

Resilience4J

Donc Resilience4J.

Resilience4J est un récent my boom qui facilite l'utilisation des modèles suivants pour améliorer la stabilité du système dans les architectures distribuées:

Pour réaliser le délai d'expiration de l'appel de méthode, utilisez TimeLimiter. Comment utiliser CircuitBreaker sera bientôt un succès, mais il n'y a pas beaucoup d'informations sur TimeLimiter, donc je vais l'écrire comme un mémorandum pour moi-même.

private final ExecutorService threadPool = Executors.newCachedThreadPool();

private final TimeLimiterConfig config = TimeLimiterConfig.custom()
        .timeoutDuration(Duration.ofMillis(1000))
        .cancelRunningFuture(true)
        .build();

private String callAnotherWorld() {

    Callable<String> callable = TimeLimiter
        .decorateFutureSupplier(
            TimeLimiter.of(config),
            () -> threadPool.submit(() -> innerCallAnotherWorld())
        );

    return Try.of(callable::call)
            .getOrElse("");
}

Ce ne sera pas trop court, mais cela éliminera la gestion des captures et des exceptions et le code se concentrera sur la logique métier.

Les fonctions fournies par Resilience4J sont des modèles de conception essentiels dans l'environnement distribué d'aujourd'hui et sont très pédagogiques.

Recommended Posts

Resilience4j TimeLimiter expire les appels de méthode
16 Correspond à l'invocation de méthode
20 Correspond aux appels de méthode statiques
Prise en compte de la méthode des temps