[JAVA] Réessayer avec Feign (OpenFeign)

Dans l'article précédent (https://qiita.com/totto357/items/90f9276697edec08a4ba), j'ai présenté comment créer un client HTTP en utilisant uniquement des annotations, mais dans le client HTTP réel, il est nécessaire de réessayer. il y a. En fait, il peut y avoir de nombreux projets qui créent une classe de client HTTP avec RestTemplate et y effectuent un nouveau traitement.

Réalisons ce processus de nouvelle tentative avec ʻOpen Feign`.

Comment ça fonctionne

Le traitement des nouvelles tentatives ʻOpenFeign` peut être réalisé en implémentant deux interfaces.

ErrorDecoder

ʻErrorDecoder est une interface pour décider quel type d'exception est lancé lorsqu'une réponse autre que les 200 est renvoyée dans une requête HTTP.

A titre d'exemple, implémentons une classe qui renvoie ʻIllegalArgumentExceptionquand400`.

class IllegalArgumentExceptionOn404Decoder implements ErrorDecoder {
    @Override
    public Exception decode(String methodKey, Response response) {
        if (response.status() == 400) {
            throw new IllegalArgumentException("C'est 400.");
        }
        return new ErrorDecoder.Default().decode(methodKey, response);
    }
}

En gros, je pense que vous pouvez changer le comportement en regardant response.status (). Si vous lancez RetryableException dans decode, ce qui suit` Retryer.continueOrPropagate «Est appelé.

Retryer

Retryer est appelé lorsque RetryableException est lancé, et est une interface pour gérer l'intervalle de relance et le nombre de fois.

La valeur par défaut Retryer est Retryer.Default essaie 5 fois en 100 ms C'est censé être fait.

Si vous souhaitez uniquement modifier l'intervalle de relance et le nombre de fois, je pense que "Retryer.Default" est suffisant.

Comment utiliser

Spécifiez la classe d'exécution des deux interfaces ci-dessus dans ʻapplication.yml`.

feign:
  client:
    config:
      {{yourFeignName}}:
        errorDecode: com.example.feign.MyErrorDecoder
        retryer: com.example.feign.MyRetryer

Définissez yourFeignName sur celui spécifié dans FeignClient.name. Si vous séparez «FeignClient.name», vous pouvez séparer le processus de nouvelle tentative pour chaque «nom».

échantillon

Ajoutons un processus de nouvelle tentative au précédent Exemple de météo.

MyErrorDecoder.java Dans cet exemple, il est tenté de réessayer lorsque «503» et «504».

package com.example.ofc.feign;

import org.springframework.web.client.RestClientException;

import feign.Response;
import feign.RetryableException;
import feign.codec.ErrorDecoder;

public class MyErrorDecoder implements ErrorDecoder {

    @Override
    public Exception decode(String methodKey, Response response) {
        RestClientException cause = new RestClientException(response.toString());

        final int status = response.status();
        if (status == 503 || status == 504) {
            return new RetryableException(methodKey, cause, null);
        }

        return cause;
    }

}

MyRetryer.java Je viens de modifier un peu Retryer.Default.

package com.example.ofc.feign;

import static java.util.concurrent.TimeUnit.SECONDS;

import feign.RetryableException;
import feign.Retryer;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MyRetryer implements Retryer {

    private final int maxAttempts;
    private final long period;
    private final long maxPeriod;
    int attempt;
    long sleptForMillis;

    public MyRetryer() {
        this(100, SECONDS.toMillis(1), 3);
    }

    public MyRetryer(long period, long maxPeriod, int maxAttempts) {
        this.period = period;
        this.maxPeriod = maxPeriod;
        this.maxAttempts = maxAttempts;
        this.attempt = 1;
    }

    // visible for testing;
    protected long currentTimeMillis() {
        return System.currentTimeMillis();
    }

    @Override
    public void continueOrPropagate(RetryableException e) {
        log.info("Réessayer le traitement");
        if (attempt++ >= maxAttempts) {
            throw e;
        }

        long interval;
        if (e.retryAfter() != null) {
            interval = e.retryAfter().getTime() - currentTimeMillis();
            if (interval > maxPeriod) {
                interval = maxPeriod;
            }
            if (interval < 0) {
                return;
            }
        } else {
            interval = nextMaxInterval();
        }
        try {
            Thread.sleep(interval);
        } catch (InterruptedException ignored) {
            Thread.currentThread().interrupt();
            throw e;
        }
        sleptForMillis += interval;
    }

    /**
     * Calculates the time interval to a retry attempt. <br>
     * The interval increases exponentially
     * with each attempt, at a rate of nextInterval *= 1.5 (where 1.5 is the
     * backoff factor), to the
     * maximum interval.
     *
     * @return time in nanoseconds from now until the next attempt.
     */
    long nextMaxInterval() {
        long interval = (long) (period * Math.pow(1.5, attempt - 1));
        return interval > maxPeriod ? maxPeriod : interval;
    }

    @Override
    public Retryer clone() {
        return new MyRetryer(period, maxPeriod, maxAttempts);
    }
}

application.yml Spécifiez les deux classes d'implémentation ci-dessus.

server:
  port: 8088
  application:
    name: open-feign-client

feign:
  client:
    config:
      weather:
        errorDecoder: com.example.ofc.feign.MyErrorDecoder
        retryer: com.example.ofc.feign.MyRetryer

Résumé

J'ai essayé de réaliser une nouvelle tentative de traitement avec ʻOpenFeign`. Il semble facile à réparer car il peut être réalisé sans toucher au client API lui-même.

Cliquez ici pour cet exemple https://github.com/totto357/open-feign-client-example

Recommended Posts

Réessayer avec Feign (OpenFeign)
Implémenter le client API avec juste des annotations à l'aide de Feign (OpenFeign)
Comment réaliser le téléchargement de fichiers avec Feign
Comment réaliser le téléchargement de fichiers avec Feign