Try-catch est conçu pour aider à la gestion des exceptions. Cela signifie en quelque sorte que cela aidera notre système à être plus robuste: essayez de récupérer d'un événement imprévu.
Nous soupçonnons que quelque chose pourrait se produire lors de l'exécution et de l'instruction (envoi d'un message), afin que cela soit inclus dans l'essai. Si quelque chose d'inattendu se produit, nous pouvons faire quelque chose: nous écrivons la prise. Je ne pense pas que nous ayons appelé pour simplement enregistrer l'exception. Je pense que le blocage est destiné à nous donner l’occasion de nous remettre de l’erreur.
Maintenant, supposons que nous récupérions de l'erreur parce que nous pourrions réparer ce qui n'allait pas. Ça pourrait être super sympa de faire un nouvel essai:
try{ some_instruction(); }
catch (NearlyUnexpectedException e){
fix_the_problem();
retry;
}
Cela tomberait rapidement dans l'éternelle boucle, mais supposons que le problème fix_the_problem retourne true, puis nous réessayons. Étant donné qu’il n’existe pas de solution de ce type en Java, comment VOUS résoudre ce problème? Quel serait votre meilleur code de conception pour résoudre ce problème?
C'est comme une question philosophique, étant donné que je sais déjà que ce que je demande n'est pas directement pris en charge par Java.
Vous devez inclure votre try-catch
dans une boucle while
comme ceci: -
int count = 0;
int maxTries = 3;
while(true) {
try {
// Some Code
// break out of loop, or return, on success
} catch (SomeException e) {
// handle exception
if (++count == maxTries) throw e;
}
}
J'ai pris count
et maxTries
pour éviter de courir dans une boucle infinie, au cas où l'exception continue de se produire dans votre try block
.
Solution "d'entreprise" obligatoire:
public abstract class Operation {
abstract public void doIt();
public void handleException(Exception cause) {
//default impl: do nothing, log the exception, etc.
}
}
public class OperationHelper {
public static void doWithRetry(int maxAttempts, Operation operation) {
for (int count = 0; count < maxAttempts; count++) {
try {
operation.doIt();
count = maxAttempts; //don't retry
} catch (Exception e) {
operation.handleException(e);
}
}
}
}
Et appeler:
OperationHelper.doWithRetry(5, new Operation() {
@Override public void doIt() {
//do some stuff
}
@Override public void handleException(Exception cause) {
//recover from the Exception
}
});
Comme d'habitude, la meilleure conception dépend des circonstances. D'habitude, j'écris quelque chose comme:
for (int retries = 0;; retries++) {
try {
return doSomething();
} catch (SomeException e) {
if (retries < 6) {
continue;
} else {
throw e;
}
}
}
Bien que try/catch
dans while
soit bien connu et bonne stratégie, je tiens à vous suggérer un appel récursif:
void retry(int i, int limit) {
try {
} catch (SomeException e) {
// handle exception
if (i >= limit) {
throw e; // variant: wrap the exception, e.g. throw new RuntimeException(e);
}
retry(i++, limit);
}
}
Vous pouvez utiliser les annotations AOP et Java de jcabi-aspects (je suis développeur):
@RetryOnFailure(attempts = 3, delay = 5)
public String load(URL url) {
return url.openConnection().getContent();
}
Vous pouvez également utiliser les annotations @Loggable
et @LogException
.
Votre scénario exact géré via Failsafe :
RetryPolicy retryPolicy = new RetryPolicy()
.retryOn(NearlyUnexpectedException.class);
Failsafe.with(retryPolicy)
.onRetry((r, f) -> fix_the_problem())
.run(() -> some_instruction());
Assez simple.
La plupart de ces réponses sont essentiellement les mêmes. Le mien est aussi, mais c'est la forme que j'aime
boolean completed = false;
Throwable lastException = null;
for (int tryCount=0; tryCount < config.MAX_SOME_OPERATION_RETRIES; tryCount++)
{
try {
completed = some_operation();
break;
}
catch (UnlikelyException e) {
lastException = e;
fix_the_problem();
}
}
if (!completed) {
reportError(lastException);
}
Utilisez une boucle while
avec l'indicateur local status
. Initialisez l'indicateur en tant que false
et définissez-le sur true
lorsque l'opération réussit, par exemple. au dessous de:
boolean success = false;
while(!success){
try{
some_instruction();
success = true;
} catch (NearlyUnexpectedException e){
fix_the_problem();
}
}
Cela va continuer à réessayer jusqu'à ce qu'il réussisse.
Si vous souhaitez réessayer uniquement un certain nombre de fois, utilisez également un compteur:
boolean success = false;
int count = 0, MAX_TRIES = 10;
while(!success && count++ < MAX_TRIES){
try{
some_instruction();
success = true;
} catch (NearlyUnexpectedException e){
fix_the_problem();
}
}
if(!success){
//It wasn't successful after 10 retries
}
Cela essaiera 10 fois maximum si cela ne réussit pas jusque-là et un sortira si cela réussit avant.
Un moyen simple de résoudre le problème consiste à envelopper la boucle try/catch dans une boucle while et à maintenir un compte. De cette façon, vous pouvez éviter une boucle infinie en comparant un compte à une autre variable tout en conservant un journal de vos échecs. Ce n'est pas la solution la plus exquise, mais cela fonctionnerait.
Si cela est utile, quelques options supplémentaires à prendre en compte, toutes jeté ensemble (fichier d'arrêt au lieu d'essais, veille, poursuite d'une boucle plus grande), toutes éventuellement utiles.
bigLoop:
while(!stopFileExists()) {
try {
// do work
break;
}
catch (ExpectedExceptionType e) {
// could sleep in here, too.
// another option would be to "restart" some bigger loop, like
continue bigLoop;
}
// ... more work
}
Vous pouvez utiliser https://github.com/bnsd55/RetryCatch
Exemple:
RetryCatch retryCatchSyncRunnable = new RetryCatch();
retryCatchSyncRunnable
// For infinite retry times, just remove this row
.retryCount(3)
// For retrying on all exceptions, just remove this row
.retryOn(ArithmeticException.class, IndexOutOfBoundsException.class)
.onSuccess(() -> System.out.println("Success, There is no result because this is a runnable."))
.onRetry((retryCount, e) -> System.out.println("Retry count: " + retryCount + ", Exception message: " + e.getMessage()))
.onFailure(e -> System.out.println("Failure: Exception message: " + e.getMessage()))
.run(new ExampleRunnable());
Au lieu de new ExampleRunnable()
, vous pouvez transmettre votre propre fonction anonyme.
voici ma solution avec une approche très simple!
while (true) {
try {
/// Statement what may cause an error;
break;
} catch (Exception e) {
}
}
Voici une approche réutilisable et plus générique pour Java 8+ qui ne nécessite pas de bibliothèques externes:
public interface IUnreliable<T extends Exception>
{
void tryRun ( ) throws T;
}
public static <T extends Exception> void retry (int retryCount, IUnreliable<T> runnable) throws T {
for (int retries = 0;; retries++) {
try {
runnable.tryRun();
return;
} catch (Exception e) {
if (retries < retryCount) {
continue;
} else {
throw e;
}
}
}
}
Usage:
@Test
public void demo() throws IOException {
retry(3, () -> {
new File("/tmp/test.txt").createNewFile();
});
}
Je ne suis pas sûr que ce soit la manière "professionnelle" de le faire et je ne suis pas tout à fait sûr que cela fonctionne pour tout.
boolean gotError = false;
do {
try {
// Code You're Trying
} catch ( FileNotFoundException ex ) {
// Exception
gotError = true;
}
} while ( gotError = true );
Utilisez un do-while pour concevoir un nouveau bloc.
boolean successful = false;
int maxTries = 3;
do{
try {
something();
success = true;
} catch(Me ifUCan) {
maxTries--;
}
} while (!successful || maxTries > 0)
C'est une vieille question mais une solution est toujours pertinente. Voici ma solution générique dans Java 8 sans utiliser de bibliothèque tierce:
public interface RetryConsumer<T> {
T evaluate() throws Throwable;
}
public interface RetryPredicate<T> {
boolean shouldRetry(T t);
}
public class RetryOperation<T> {
private RetryConsumer<T> retryConsumer;
private int noOfRetry;
private int delayInterval;
private TimeUnit timeUnit;
private RetryPredicate<T> retryPredicate;
private List<Class<? extends Throwable>> exceptionList;
public static class OperationBuilder<T> {
private RetryConsumer<T> iRetryConsumer;
private int iNoOfRetry;
private int iDelayInterval;
private TimeUnit iTimeUnit;
private RetryPredicate<T> iRetryPredicate;
private Class<? extends Throwable>[] exceptionClasses;
private OperationBuilder() {
}
public OperationBuilder<T> retryConsumer(final RetryConsumer<T> retryConsumer) {
this.iRetryConsumer = retryConsumer;
return this;
}
public OperationBuilder<T> noOfRetry(final int noOfRetry) {
this.iNoOfRetry = noOfRetry;
return this;
}
public OperationBuilder<T> delayInterval(final int delayInterval, final TimeUnit timeUnit) {
this.iDelayInterval = delayInterval;
this.iTimeUnit = timeUnit;
return this;
}
public OperationBuilder<T> retryPredicate(final RetryPredicate<T> retryPredicate) {
this.iRetryPredicate = retryPredicate;
return this;
}
@SafeVarargs
public final OperationBuilder<T> retryOn(final Class<? extends Throwable>... exceptionClasses) {
this.exceptionClasses = exceptionClasses;
return this;
}
public RetryOperation<T> build() {
if (Objects.isNull(iRetryConsumer)) {
throw new RuntimeException("'#retryConsumer:RetryConsumer<T>' not set");
}
List<Class<? extends Throwable>> exceptionList = new ArrayList<>();
if (Objects.nonNull(exceptionClasses) && exceptionClasses.length > 0) {
exceptionList = Arrays.asList(exceptionClasses);
}
iNoOfRetry = iNoOfRetry == 0 ? 1 : 0;
iTimeUnit = Objects.isNull(iTimeUnit) ? TimeUnit.MILLISECONDS : iTimeUnit;
return new RetryOperation<>(iRetryConsumer, iNoOfRetry, iDelayInterval, iTimeUnit, iRetryPredicate, exceptionList);
}
}
public static <T> OperationBuilder<T> newBuilder() {
return new OperationBuilder<>();
}
private RetryOperation(RetryConsumer<T> retryConsumer, int noOfRetry, int delayInterval, TimeUnit timeUnit,
RetryPredicate<T> retryPredicate, List<Class<? extends Throwable>> exceptionList) {
this.retryConsumer = retryConsumer;
this.noOfRetry = noOfRetry;
this.delayInterval = delayInterval;
this.timeUnit = timeUnit;
this.retryPredicate = retryPredicate;
this.exceptionList = exceptionList;
}
public T retry() throws Throwable {
T result = null;
int retries = 0;
while (retries < noOfRetry) {
try {
result = retryConsumer.evaluate();
if (Objects.nonNull(retryPredicate)) {
boolean shouldItRetry = retryPredicate.shouldRetry(result);
if (shouldItRetry) {
retries = increaseRetryCountAndSleep(retries);
} else {
return result;
}
} else {
// no retry condition defined, no exception thrown. This is the desired result.
return result;
}
} catch (Throwable e) {
retries = handleException(retries, e);
}
}
return result;
}
private int handleException(int retries, Throwable e) throws Throwable {
if (exceptionList.contains(e.getClass()) || (exceptionList.isEmpty())) {
// exception is excepted, continue retry.
retries = increaseRetryCountAndSleep(retries);
if (retries == noOfRetry) {
// evaluation is throwing exception, no more retry left. Throw it.
throw e;
}
} else {
// unexpected exception, no retry required. Throw it.
throw e;
}
return retries;
}
private int increaseRetryCountAndSleep(int retries) {
retries++;
if (retries < noOfRetry && delayInterval > 0) {
try {
timeUnit.sleep(delayInterval);
} catch (InterruptedException ignore) {
Thread.currentThread().interrupt();
}
}
return retries;
}
}
Ayons un cas de test comme:
@Test
public void withPredicateAndException() {
AtomicInteger integer = new AtomicInteger();
try {
Integer result = RetryOperation.<Integer>newBuilder()
.retryConsumer(() -> {
int i = integer.incrementAndGet();
if (i % 2 == 1) {
throw new NumberFormatException("Very odd exception");
} else {
return i;
}
})
.noOfRetry(10)
.delayInterval(10, TimeUnit.MILLISECONDS)
.retryPredicate(value -> value <= 6)
.retryOn(NumberFormatException.class, EOFException.class)
.build()
.retry();
Assert.assertEquals(8, result.intValue());
} catch (Throwable throwable) {
Assert.fail();
}
}
Le problème avec les solutions restantes est que, la fonction de correspondant essaie en continu sans intervalle de temps, inondant ainsi la pile.
Pourquoi ne pas simplement try
er seulement chaque seconde et ad eternum ?
Voici une solution utilisant setTimeout
et une fonction récursive:
(function(){
try{
Run(); //tries for the 1st time, but Run() as function is not yet defined
}
catch(e){
(function retry(){
setTimeout(function(){
try{
console.log("trying...");
Run();
console.log("success!");
}
catch(e){
retry(); //calls recursively
}
}, 1000); //tries every second
}());
}
})();
//after 5 seconds, defines Run as a global function
var Run;
setTimeout(function(){
Run = function(){};
}, 5000);
Remplacez la fonction Run()
par la fonction ou le code que vous souhaitez retry
chaque seconde.
Tout ce que Try-Catch fait, c’est que votre programme échoue normalement. Dans une instruction catch, vous essayez généralement de consigner l'erreur et, éventuellement, d'annuler les modifications.
bool finished = false;
while(finished == false)
{
try
{
//your code here
finished = true
}
catch(exception ex)
{
log.error("there was an error, ex");
}
}
Spring AOP et solution basée sur des annotations:
Utilisation (@RetryOperation
est notre annotation personnalisée pour le travail):
@RetryOperation(retryCount = 1, waitSeconds = 10)
boolean someMethod() throws Exception {
}
Pour ce faire, nous avons besoin de deux choses: 1. une interface d'annotation, et 2. un aspect de ressort. Voici un moyen de les implémenter:
L'interface d'annotation:
import Java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryOperation {
int retryCount();
int waitSeconds();
}
L'aspect printanier:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import Java.lang.reflect.Method;
@Aspect @Component
public class RetryAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(RetryAspect.class);
@Around(value = "@annotation(RetryOperation)")
public Object retryOperation(ProceedingJoinPoint joinPoint) throws Throwable {
Object response = null;
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
RetryOperation annotation = method.getAnnotation(RetryOperation.class);
int retryCount = annotation.retryCount();
int waitSeconds = annotation.waitSeconds();
boolean successful = false;
do {
try {
response = joinPoint.proceed();
successful = true;
} catch (Exception ex) {
LOGGER.info("Operation failed, retries remaining: {}", retryCount);
retryCount--;
if (retryCount < 0) {
throw ex;
}
if (waitSeconds > 0) {
LOGGER.info("Waiting for {} second(s) before next retry", waitSeconds);
Thread.sleep(waitSeconds * 1000l);
}
}
} while (!successful);
return response;
}
}
https://github.com/tusharmndr/retry-function-wrapper/tree/master/src/main/Java/io
int MAX_RETRY = 3;
RetryUtil.<Boolean>retry(MAX_RETRY,() -> {
//Function to retry
return true;
});
Je sais qu'il y a déjà beaucoup de réponses similaires ici, et la mienne n'est pas très différente, mais je vais l'afficher quand même, car il s'agit d'un cas/problème spécifique.
Lorsque vous traitez avec le facebook Graph API
dans PHP
, vous obtenez parfois une erreur, mais réessayer immédiatement la même chose donnera un résultat positif (pour diverses raisons magiques Internet dépassant le cadre de cette question). Dans ce cas, il n'est pas nécessaire de corriger les erreurs, mais simplement d'essayer à nouveau car il y avait une sorte d '"erreur facebook".
Ce code est utilisé immédiatement après la création d'une session facebook:
//try more than once because sometimes "facebook error"
$attempt = 3;
while($attempt-- > 0)
{
// To validate the session:
try
{
$facebook_session->validate();
$attempt = 0;
}
catch (Facebook\FacebookRequestException $ex)
{
// Session not valid, Graph API returned an exception with the reason.
if($attempt <= 0){ echo $ex->getMessage(); }
}
catch (\Exception $ex)
{
// Graph API returned info, but it may mismatch the current app or have expired.
if($attempt <= 0){ echo $ex->getMessage(); }
}
}
De plus, en comptant le nombre de boucles for
à zéro ($attempt--
), il est très facile de modifier le nombre de tentatives ultérieures.