Bagaimana Anda menerapkan re-try-catch?


203

Try-catch dimaksudkan untuk membantu dalam penanganan pengecualian. Ini berarti, entah bagaimana, itu akan membantu sistem kami menjadi lebih kuat: cobalah untuk pulih dari peristiwa yang tidak terduga.

Kami menduga sesuatu mungkin terjadi ketika mengeksekusi dan instruksi (mengirim pesan), sehingga terlampir dalam percobaan. Jika sesuatu yang hampir tak terduga terjadi, kita dapat melakukan sesuatu: kita menulis tangkapan. Saya tidak berpikir kami dipanggil untuk hanya mencatat pengecualian. Saya kira catch block dimaksudkan untuk memberi kita kesempatan untuk pulih dari kesalahan.

Sekarang, katakanlah kita pulih dari kesalahan karena kita dapat memperbaiki apa yang salah. Sangat bagus untuk mencoba ulang:

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

Ini akan dengan cepat jatuh dalam loop abadi, tetapi katakanlah fix_the_problem mengembalikan true, lalu kita coba lagi. Karena tidak ada hal seperti itu di Jawa, bagaimana ANDA akan menyelesaikan masalah ini? Apa kode desain terbaik Anda untuk menyelesaikan ini?

Ini seperti pertanyaan filosofis, mengingat bahwa saya sudah tahu apa yang saya minta tidak secara langsung didukung oleh Jawa.


5
Pengecualian seperti apa itu?
Bhesh Gurung

23
Saya suka nama pengecualian Anda. ;)
Rohit Jain

Sebenarnya, tidak ada banyak pengecualian dari mana Anda dapat memulihkan. Saya akui motivasi awal saya bukan pengecualian yang sebenarnya, tetapi cara untuk menghindari jika itu akan terjadi hampir tidak pernah: saya mencoba remove()dari java.util.Queue, yang mana dan InvalidElementExceptionkapan antriannya kosong. Alih-alih bertanya apakah itu kosong, saya mengecam tindakan dalam try-catch (yang dalam konkurensi menjadi wajib bahkan dengan sebelumnya jika). Dalam kasus seperti itu, di catchblok saya akan meminta untuk mengisi ulang antrian dengan lebih banyak elemen lalu, coba lagi. Voila.
Andres Farias

1
Saya bisa melihat cara biasa melakukan ini adalah untuk akses DB, jika koneksi gagal terhubung kembali, jika gagal maka lontarkan pengecualian utama jika tidak coba lagi panggilan. Seperti telah dikatakan kita bisa melakukannya dalam satu lingkaran dengan tanda centang di bagian bawah jika (kesalahan <> 0) kemudian kembali jika tidak pecah;
Theresa Forster

Jawaban:


304

Anda harus melampirkan lingkaran try-catchdi dalam Anda whileseperti ini: -

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

Saya telah mengambil countdan maxTriesmenghindari berlari ke loop yang tak terbatas, kalau - kalau pengecualian terus terjadi di Anda try block.


3
Saya berpikir dalam hal seperti ini pada awalnya, tanpa maxTries. Terima kasih atas jawabannya!
Andres Farias

6
@AndresFarias .. Ya, poin terpenting dalam jawaban ini adalah memasukkan a maxTries. Jika tidak maka akan berjalan menjadi infinite loopjika pengguna terus memberikan input yang salah, dan karenanya tidak akan keluar. Sama-sama. :)
Rohit Jain

terima kasih untuk ini - itu hanya menyelamatkan saya dari keharusan menulis beberapa kode yang sangat degil!
David Holiday

2
Apakah mungkin menambahkan fungsi Thread.sleep () di dalam tangkapan di sini. Karena dalam beberapa kasus suka menunggu respons halaman di perpustakaan Selenium yang menjadi kritis. Terima kasih.
Suat Atan PhD

2
Bagus sekali! Untuk pemula: Jika Anda mendapatkan loop tak terbatas positif, periksa apakah Anda menambahkan "break;" di akhir di blok "coba".
Krzysztof Walczewski

59

Solusi "giat" wajib:

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

Dan untuk menelepon:

OperationHelper.doWithRetry(5, new Operation() {
    @Override public void doIt() {
        //do some stuff
    }
    @Override public void handleException(Exception cause) {
        //recover from the Exception
    }
});

6
Anda harus melemparkan kembali pengecualian jika percobaan ulang terakhir gagal, seperti yang dilakukan pada jawaban lain yang diberikan.
cvacca

35

Seperti biasa, desain terbaik tergantung pada keadaan tertentu. Namun biasanya, saya menulis sesuatu seperti:

for (int retries = 0;; retries++) {
    try {
        return doSomething();
    } catch (SomeException e) {
        if (retries < 6) {
            continue;
        } else {
            throw e;
        }
    }
}

Tunggu, mengapa tidak memiliki kondisi di dalam deklarasi for loop seperti: for (int retries = 0; retries <6; retries ++) ??
Didier A.

8
Karena saya hanya ingin melempar pada upaya terakhir, dan oleh karena itu catch catch memerlukan kondisi itu, membuat kondisi di untuk berlebihan.
meriton

1
Saya tidak berpikir itu continuediperlukan di sana .. Dan Anda bisa membalik kondisi if.
Koray Tugay

19

Meskipun try/catchmenjadi whilestrategi terkenal dan baik saya ingin menyarankan Anda panggilan rekursif:

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

41
Bagaimana rekursi lebih baik daripada satu loop untuk use case ini?
Dan

7
Jejak tumpukan mungkin terlihat sedikit aneh pada yang satu ini, karena bukankah akan ada limithitungan metode yang sedang berulang? Berbeda dengan versi loop, yang akan melempar pada tingkat 'asli' ...
Clockwork-Muse

7
Tentu terlihat elegan di atas kertas tetapi saya tidak yakin rekursi adalah pendekatan yang tepat.
Thomas

3
Saya tidak mengerti mengapa rekursi di sini juga. Bagaimanapun, saya pikir itu bisa disederhanakan menjadi:void retry(int times) { (...) if (times==0) throw w; retry(times--);
sinuhepop

8
Merupakan praktik yang buruk untuk menggunakan rekursi sebagai pengganti iterasi belaka. Rekursi adalah untuk digunakan ketika Anda ingin mendorong dan mengeluarkan beberapa data.
Marquis of Lorne

19

Skenario tepat Anda ditangani melalui Failsafe :

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

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

Cukup mudah.


5
perpustakaan yang sangat bagus.
Maksim

bagi mereka yang bertanya-tanya, Anda akan memerlukan ini dalam dependensi gradle Anda - kompilasi 'net.jodah :ailsafe: 1.1.0'
Shreyas

18

Anda dapat menggunakan penjelasan AOP dan Java dari aspek jcabi (saya seorang pengembang):

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

Anda juga dapat menggunakan @Loggabledan @LogExceptionanotasi.


Wow ! Kedengarannya Mewah! :)
Alind Billore

Harus menjadi jawaban teratas.
Mohamed Taher Alrefaie

2
apakah ada cara untuk "memperbaiki" kesalahan ketika upaya gagal (lakukan beberapa adopsi yang dapat memperbaiki upaya berikutnya)? lihat pertanyaan: fix_the_problem();di blok tangkap
warch

Mengingat jumlah masalah terbuka dan waktu yang berlalu untuk bug yang diakui tidak diperbaiki, saya tidak akan bergantung pada perpustakaan ini.
Michael Lihs

6

Sebagian besar jawaban ini pada dasarnya sama. Milik saya juga, tapi ini adalah bentuk yang saya suka

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

Salah satu kelemahannya adalah Anda juga menelepon fix_the_problemsetelah upaya terakhir. Itu bisa menjadi operasi yang mahal dan bisa menghabiskan waktu.
Joachim Sauer

2
@ JoachimSauer Benar. Anda bisa if (tryCount < max) fix()- tetapi ini adalah format pendekatan umum; detailnya akan tergantung pada kasus tertentu. Ada juga Retryer berbasis jambu yang telah saya lihat.
Stephen P

4

Spring AOP dan solusi berbasis anotasi:

Penggunaan ( @RetryOperationadalah anotasi khusus kami untuk pekerjaan itu):

@RetryOperation(retryCount = 1, waitSeconds = 10)
boolean someMethod() throws Exception {
}

Kita membutuhkan dua hal untuk mencapai hal ini: 1. antarmuka anotasi, dan 2. aspek pegas. Inilah salah satu cara untuk mengimplementasikan ini:

Antarmuka Anotasi:

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryOperation {
    int retryCount();
    int waitSeconds();
}

Aspek Musim Semi:

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

3

Gunakan whilelingkaran dengan statusbendera lokal . Inisialisasi bendera sebagai falsedan atur trueketika operasi berhasil, misalnya di bawah ini:

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

Ini akan terus mencoba ulang hingga berhasil.

Jika Anda ingin mencoba lagi hanya beberapa kali saja maka gunakan penghitung juga:

  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
  }

Ini akan mencoba maks 10 kali jika tidak berhasil sampai kemudian akan keluar jika berhasil sebelumnya.


Daripada memeriksa !successsaat Anda, Anda bisa keluar saat ketika kesuksesan itu benar.
Rohit Jain

1
@RohitJain: Terlihat lebih bersih bagi saya.
Yogendra Singh

@YogendraSingh .. Aneh. karena Anda tidak mengubah successtempat Anda di catch. Jadi sepertinya berlebihan untuk memeriksanya, pada setiap putaran catch.
Rohit Jain

@RohitJain: Catch hanya mengoreksi data. Ini akan kembali dan menjalankan pernyataan lagi. Jika berhasil, itu akan mengubah success. Cobalah.
Yogendra Singh

3

Ini adalah pertanyaan lama tetapi solusinya masih relevan. Berikut ini adalah solusi umum saya di Java 8 tanpa menggunakan perpustakaan pihak ketiga:

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

Mari kita uji kasus seperti:

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

ide yang bagus, pembangun mondar-mandir itu!
HankTheTank

2

Cara sederhana untuk memecahkan masalah ini adalah dengan membungkus try / catch dalam loop sementara dan mempertahankan hitungan. Dengan cara ini Anda bisa mencegah loop tak terbatas dengan memeriksa hitungan terhadap beberapa variabel lain sambil mempertahankan log kegagalan Anda. Itu bukan solusi yang paling indah, tetapi itu akan berhasil.


1

Gunakan do-while untuk merancang blok coba ulang.

boolean successful = false;
int maxTries = 3;
do{
  try {
    something();
    success = true;
  } catch(Me ifUCan) {
    maxTries--;
  }
} while (!successful || maxTries > 0)

2
Kode harus membuang pengecualian asli jika tidak berhasil
lilalinux

1

Dalam hal ini berguna, beberapa pilihan lain untuk dipertimbangkan, semua dilemparkan bersama (stopfile alih-alih coba lagi, tidur, lanjutkan loop yang lebih besar) semua mungkin membantu.

 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
}

Pemilih yang kurang silakan tinggalkan komentar mengapa, terima kasih!
rogerdpack

1
Ini adalah ketidaktahuan belaka untuk downvote dan tidak mengutip alasan.
xploreraj

tidur di sana tidak jelas karena loop sementara tidak akan menunggu
João Pimentel Ferreira

1

Anda dapat menggunakan https://github.com/bnsd55/RetryCatch

Contoh:

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

Alih-alih new ExampleRunnable()Anda dapat melewati fungsi anonim Anda sendiri.


1

Jika tidak semua pengecualian memerlukan percobaan ulang, hanya beberapa saja. Dan jika setidaknya satu percobaan harus dilakukan, Berikut adalah metode utilitas alternatif:

void runWithRetry(Runnable runnable, Class<Exception> exClass, int maxRetries) {
        Exception err = null;
        do {
            maxRetries--;
            try {
                runnable.run();
                err = null;
            } catch (Exception e) {
                if(exClass.isAssignableFrom(e.getClass())){
                    err = e;
                }else {
                    throw e;
                }
            }
        } while (err != null && maxRetries > 0);

        if (err != null) {
            throw err;
        }
    }

Pemakaian:

    runWithRetry(() -> {
       // do something
    }, TimeoutException.class, 5)

0

Semua yang dilakukan Try-Catch memungkinkan program Anda gagal dengan anggun. Dalam pernyataan tangkap, Anda biasanya mencoba mencatat kesalahan, dan mungkin memutar kembali perubahan jika perlu.

bool finished = false;

while(finished == false)
{
    try
    {
        //your code here
        finished = true
    }
    catch(exception ex)
    {
        log.error("there was an error, ex");
    }
}

apakah maksud Anda berlawanan (!finished)?
Sam saya berkata Reinstate Monica

1
@RohitJain sepertinya terlalu banyak while(finished). Saya lebih suka menggunakan versi yang lebih verbose.
Sam saya berkata Reinstate Monica

3
Bagaimana di bumi ini while(!finished)terlihat seperti while (finished)??
Rohit Jain

@Rohit Karena hanya satu karakter yang berbeda. Mereka semua dikompilasi ke hal yang sama. Di C #, saya menggunakan metode ekstensi String IsPopulated()yang baru saja kembali !IsNullOrEmpty()untuk memastikan bahwa maksud saya dipahami oleh semua pengembang.
Michael Blackburn

0

Saya tahu sudah ada banyak jawaban serupa di sini, dan jawaban saya tidak jauh berbeda, tetapi saya tetap akan mempostingnya karena ini berkaitan dengan kasus / masalah tertentu.

Ketika berhadapan dengan facebook Graph APIin PHPAnda terkadang mendapatkan kesalahan, tetapi segera mencoba kembali hal yang sama akan memberikan hasil positif (untuk berbagai magis alasan internet yang berada di luar lingkup pertanyaan ini). Dalam hal ini tidak perlu memperbaiki kesalahan apa pun, tetapi cukup mencoba lagi karena ada semacam "kesalahan facebook".

Kode ini digunakan segera setelah membuat sesi 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(); }
    }
}

Juga, dengan memiliki forloop menghitung mundur ke nol ( $attempt--) membuatnya cukup mudah untuk mengubah jumlah upaya di masa depan.


0

berikut ini solusi saya dengan pendekatan yang sangat sederhana!

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

                    }
                }

1
silakan lihat jawaban @Rohit Jain yang lebih spesifik dan bukan loop tak terbatas dalam kasus negatif.
Chandra Shekhar

0

Saya tidak yakin apakah ini cara "Profesional" untuk melakukannya dan saya tidak sepenuhnya yakin apakah itu berfungsi untuk semuanya.

boolean gotError = false;

do {
    try {
        // Code You're Trying
    } catch ( FileNotFoundException ex ) {
        // Exception
        gotError = true;
    }
} while ( gotError = true );


0

Di sini pendekatan yang dapat digunakan kembali dan lebih umum untuk Java 8+ yang tidak memerlukan perpustakaan eksternal:

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

Pemakaian:

@Test
public void demo() throws IOException {
    retry(3, () -> {
        new File("/tmp/test.txt").createNewFile();
    });
}

0

Masalah dengan solusi yang tersisa adalah bahwa, fungsi koresponden mencoba terus menerus tanpa interval waktu di antaranya, sehingga membanjiri tumpukan.

Mengapa tidak tryhanya setiap detik dan iklan saja ?

Di sini solusi menggunakan setTimeoutdan fungsi rekursif:

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

Ganti fungsi Run()dengan fungsi atau kode yang ingin Anda ulang trysetiap detik.


0

Cobalah menggunakan springs @ annotation, metode di bawah ini akan mencoba lagi selama 3 upaya ketika RuntimeException terjadi

@Retryable(maxAttempts=3,value= {RuntimeException.class},backoff = @Backoff(delay = 500))
public void checkSpringRetry(String str) {
    if(StringUtils.equalsIgnoreCase(str, "R")) {
        LOGGER.info("Inside retry.....!!");
        throw new RuntimeException();
    }
}

0

Cuplikan di bawah ini menjalankan beberapa cuplikan kode. Jika Anda mendapatkan kesalahan saat menjalankan cuplikan kode, tidur selama M milidetik dan coba lagi. Tautan referensi .

public void retryAndExecuteErrorProneCode(int noOfTimesToRetry, CodeSnippet codeSnippet, int sleepTimeInMillis)
  throws InterruptedException {

 int currentExecutionCount = 0;
 boolean codeExecuted = false;

 while (currentExecutionCount < noOfTimesToRetry) {
  try {
   codeSnippet.errorProneCode();
   System.out.println("Code executed successfully!!!!");
   codeExecuted = true;
   break;
  } catch (Exception e) {
   // Retry after 100 milliseconds
   TimeUnit.MILLISECONDS.sleep(sleepTimeInMillis);
   System.out.println(e.getMessage());
  } finally {
   currentExecutionCount++;
  }
 }

 if (!codeExecuted)
  throw new RuntimeException("Can't execute the code within given retries : " + noOfTimesToRetry);
}

0

Berikut ini adalah solusi saya mirip dengan beberapa orang lain dapat membungkus suatu fungsi, tetapi memungkinkan Anda untuk mendapatkan nilai kembali fungsi, jika itu berhasil.

    /**
     * Wraps a function with retry logic allowing exceptions to be caught and retires made.
     *
     * @param function the function to retry
     * @param maxRetries maximum number of retires before failing
     * @param delay time to wait between each retry
     * @param allowedExceptionTypes exception types where if caught a retry will be performed
     * @param <V> return type of the function
     * @return the value returned by the function if successful
     * @throws Exception Either an unexpected exception from the function or a {@link RuntimeException} if maxRetries is exceeded
     */
    @SafeVarargs
    public static <V> V runWithRetriesAndDelay(Callable<V> function, int maxRetries, Duration delay, Class<? extends Exception>... allowedExceptionTypes) throws Exception {
        final Set<Class<? extends Exception>> exceptions = new HashSet<>(Arrays.asList(allowedExceptionTypes));
        for(int i = 1; i <= maxRetries; i++) {
            try {
                return function.call();
            } catch (Exception e) {
                if(exceptions.contains(e.getClass())){
                    // An exception of an expected type
                    System.out.println("Attempt [" + i + "/" + maxRetries + "] Caught exception [" + e.getClass() + "]");
                    // Pause for the delay time
                    Thread.sleep(delay.toMillis());
                }else {
                    // An unexpected exception type
                    throw e;
                }
            }
        }
        throw new RuntimeException(maxRetries + " retries exceeded");
    }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.