¿Cómo implementar un re-try-catch?


Try-catch está destinado a ayudar en el manejo de excepciones. Esto significa de alguna manera que ayudará a nuestro sistema a ser más robusto: tratar de recuperarse de un evento inesperado.

Sospechamos que algo podría suceder al ejecutar e instruir (enviar un mensaje), por lo que se incluye en el intento. Si sucede algo casi inesperado, podemos hacer algo: escribimos la captura. No creo que hayamos llamado para registrar la excepción. Creo que el bloque catch está destinado a darnos la oportunidad de recuperarse del error.

Ahora, digamos que nos recuperamos del error porque pudimos arreglar lo que estaba mal. Podría ser súper agradable volver a intentarlo:

try{ some_instruction(); }
catch (NearlyUnexpectedException e){
   fix_the_problem();
   retry;
}

Esto caería rápidamente en el bucle eterno, pero digamos que el fix_the_problem devuelve true, luego volvemos a intentarlo. Dado que no existe tal cosa en Java, ¿cómo resolverías este problema? ¿Cuál sería tu mejor código de diseño para resolver esto?

Esto es como una pregunta filosófica, dado que ya sé lo que estoy pidiendo no está soportado directamente por Java.

Author: Peter O., 2012-11-06

18 answers

Necesitas encerrar tu try-catch dentro de un bucle while así: -

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;
    }
}

He tomado count y maxTries para evitar correr en un bucle infinito, en caso de que la excepción sigue ocurriendo en su try block.

 224
Author: Rohit Jain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-04 21:56:22

Solución "emprendedora" obligatoria:

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);
            }
        }
    }
}

Y llamar:

OperationHelper.doWithRetry(5, new Operation() {
    @Override public void doIt() {
        //do some stuff
    }
    @Override public void handleException(Exception cause) {
        //recover from the Exception
    }
});
 41
Author: ach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-05 21:21:10

Como de costumbre, el mejor diseño depende de las circunstancias particulares. Por lo general, sin embargo, escribo algo como:

for (int retries = 0;; retries++) {
    try {
        return doSomething();
    } catch (SomeException e) {
        if (retries < 6) {
            continue;
        } else {
            throw e;
        }
    }
}
 31
Author: meriton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-20 08:04:57

Aunque try/catch dentro de while es una estrategia bien conocida y buena, quiero sugerirte una llamada recursiva:

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);
    }
}
 19
Author: AlexR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-05 20:44:15

Puedes usar anotaciones AOP y Java desde jcabi-aspects (soy desarrollador):

@RetryOnFailure(attempts = 3, delay = 5)
public String load(URL url) {
  return url.openConnection().getContent();
}

También puedes usar @Loggable y @LogException anotaciones.

 14
Author: yegor256,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-04 12:33:22

Su escenario exacto manejado a través de A prueba de fallos :

RetryPolicy retryPolicy = new RetryPolicy()
  .retryOn(NearlyUnexpectedException.class);

Failsafe.with(retryPolicy)
  .onRetry((r, f) -> fix_the_problem())
  .run(() -> some_instruction());

Bastante simple.

 9
Author: Jonathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-26 22:32:00

La mayoría de estas respuestas son esencialmente las mismas. La mía también, pero esta es la forma que me gusta

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);
}
 6
Author: Stephen P,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-05 21:24:27

Use un bucle while con una bandera local status. Inicialice el indicador como false y establézcalo en true cuando la operación se realice correctamente, por ejemplo, a continuación:

  boolean success  = false;
  while(!success){
     try{ 
         some_instruction(); 
         success = true;
     } catch (NearlyUnexpectedException e){
       fix_the_problem();
     }
  }

Esto seguirá volviendo a intentarlo hasta que tenga éxito.

Si desea volver a intentarlo solo un cierto número de veces, use un contador también:

  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
  }

Esto intentará máximo 10 veces si no tiene éxito hasta entonces un saldrá si su éxito antes de la mano.

 3
Author: Yogendra Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-05 20:39:39

Una forma sencilla de resolver el problema sería envolver el try/catch en un bucle while y mantener un conteo. De esta manera, podría evitar un bucle infinito comprobando un recuento contra alguna otra variable mientras mantiene un registro de sus errores. No es la solución más exquisita, pero funcionaría.

 2
Author: Jordan Kaye,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-05 20:40:12

En caso de que sea útil, un par de opciones más a considerar, todas juntas (stopfile en lugar de reintentos, sleep, continue loop más grande), todas posiblemente útiles.

 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
}
 1
Author: rogerdpack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-12 20:28:44

Puedes usar https://github.com/bnsd55/RetryCatch

Ejemplo:

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());

En lugar de new ExampleRunnable() puede pasar su propia función anónima.

 1
Author: bnsd55,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-08 13:30:40

Todo lo que hace un Try-Catch es permitir que su programa falle correctamente. En una instrucción catch, generalmente intenta registrar el error y tal vez revertir los cambios si es necesario.

bool finished = false;

while(finished == false)
{
    try
    {
        //your code here
        finished = true
    }
    catch(exception ex)
    {
        log.error("there was an error, ex");
    }
}
 0
Author: Sam I am,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-05 20:40:16

Utilice un do-while para diseñar el bloque re-try.

boolean successful = false;
int maxTries = 3;
do{
  try {
    something();
    success = true;
  } catch(Me ifUCan) {
    maxTries--;
  }
} while (!successful || maxTries > 0)
 0
Author: Rahul Malhotra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-28 07:46:04

Sé que ya hay muchas respuestas similares aquí, y la mía no es muy diferente, pero la publicaré de todos modos porque trata de un caso/tema específico.

Cuando se trata de la facebook Graph API en PHP a veces se obtiene un error, pero inmediatamente volver a intentar la misma cosa dará un resultado positivo (por varias mágicas Razones de Internet que están más allá del alcance de esta pregunta). En este caso no hay necesidad de corregir cualquier error, sino simplemente intentarlo de nuevo porque hay fue una especie de "error de Facebook".

Este código se usa inmediatamente después de crear una sesión de 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(); }
    }
}

Además, al tener la cuenta atrás del bucle for a cero ($attempt--) hace que sea bastante fácil cambiar el número de intentos en el futuro.

 0
Author: KnightHawk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-14 15:19:40

Siguiente es mi solución con un enfoque muy simple!

               while (true) {
                    try {
                        /// Statement what may cause an error;
                        break;
                    } catch (Exception e) {

                    }
                }
 0
Author: David Kayo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-19 01:56:27

No estoy seguro de si esta es la forma "Profesional" de hacerlo y no estoy del todo seguro de si funciona para todo.

boolean gotError = false;

do {
    try {
        // Code You're Trying
    } catch ( FileNotFoundException ex ) {
        // Exception
        gotError = true;
    }
} while ( gotError = true );
 0
Author: Josh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-04 01:42:57

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;
});
 0
Author: tushar Mandar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-02 12:11:18

Aquí un enfoque reutilizable y más genérico para Java 8 + que no requiere bibliotecas externas:

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;
            }
        }
    }
}

Uso:

@Test
public void demo() throws IOException {
    retry(3, () -> {
        new File("/tmp/test.txt").createNewFile();
    });
}
 0
Author: Jonas_Hess,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-20 08:39:16