คุณใช้การจับซ้ำได้อย่างไร


203

ลองจับมีไว้เพื่อช่วยในการจัดการข้อยกเว้น หมายความว่าจะช่วยให้ระบบของเรามีความแข็งแกร่งยิ่งขึ้น: พยายามกู้คืนจากเหตุการณ์ที่ไม่คาดคิด

เราสงสัยว่ามีบางอย่างอาจเกิดขึ้นเมื่อดำเนินการและคำสั่ง (ส่งข้อความ) ดังนั้นจึงถูกปิดล้อมในการลอง หากสิ่งนั้นเกือบเกิดขึ้นโดยไม่คาดคิดเราสามารถทำบางสิ่งได้: เราเขียนสิ่งที่จับได้ ฉันไม่คิดว่าเราถูกเรียกให้เข้าสู่ระบบยกเว้น สิ่งที่ฉันบล็อก catch มีขึ้นเพื่อให้โอกาสเราในการกู้คืนจากข้อผิดพลาด

ตอนนี้สมมติว่าเรากู้คืนจากข้อผิดพลาดเพราะเราสามารถแก้ไขสิ่งที่ผิด อาจเป็นเรื่องดีสุด ๆ ที่จะลองทำใหม่:

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

สิ่งนี้จะตกอยู่ในวงนิรันดร์อย่างรวดเร็ว แต่สมมุติว่า fix_the_problem คืนค่าเป็นจริงจากนั้นเราลองอีกครั้ง เนื่องจากไม่มีสิ่งเช่นนี้ใน Java คุณจะแก้ไขปัญหานี้อย่างไร สิ่งที่จะเป็นรหัสการออกแบบที่ดีที่สุดของคุณสำหรับการแก้ปัญหานี้?

นี่เป็นคำถามเชิงปรัชญาเนื่องจากฉันรู้แล้วว่าสิ่งที่ฉันขอไม่ได้รับการสนับสนุนโดยตรงจาก Java


5
มีข้อยกเว้นประเภทใด
Bhesh Gurung

23
ฉันชอบชื่อข้อยกเว้นของคุณ ;)
Rohit Jain

ในโฉนดมีข้อยกเว้นไม่มากที่คุณสามารถกู้คืนได้ ฉันยอมรับว่าแรงจูงใจเริ่มแรกของฉันไม่ใช่ข้อยกเว้นจริง แต่เป็นวิธีที่จะหลีกเลี่ยงสิ่งที่จะเกิดขึ้นแทบจะไม่: ฉันลองremove()จาก a java.util.Queueซึ่ง thorws และInvalidElementExceptionเมื่อคิวว่างเปล่า แทนที่จะถามว่ามันว่างเปล่าหรือเปล่าฉันกลับไปหาการกระทำในแบบลองจับ (ซึ่งอยู่ภายใต้การเห็นพ้องด้วยกันกลายเป็นภาคบังคับแม้จะก่อนหน้านี้ถ้า) ในกรณีเช่นนี้ในcatchบล็อกฉันจะขอให้เติมคิวด้วยองค์ประกอบเพิ่มเติมแล้วลองอีกครั้ง voila
Andres Farias

1
ฉันสามารถดูวิธีปกติของการทำเช่นนี้ได้สำหรับการเข้าถึงฐานข้อมูลถ้าการเชื่อมต่อล้มเหลวในการเชื่อมต่ออีกครั้งหากมันล้มเหลวให้โยนข้อยกเว้นที่สำคัญมิฉะนั้นลองโทรอีกครั้ง ดังที่ได้กล่าวไปแล้วว่าเราสามารถทำมันด้วยการวนรอบที่มีการตรวจสอบที่ด้านล่างถ้า (ข้อผิดพลาด <> 0) จากนั้นกลับไปเป็นอย่างอื่นแตก;
เทเรซ่าฟอสเตอร์

คำตอบ:


304

คุณต้องใส่วงtry-catchในของคุณwhileดังนี้: -

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

ฉันได้นำcountและmaxTriesเพื่อหลีกเลี่ยงการทำงานในวง จำกัด try blockในกรณียกเว้นช่วยในการเกิดขึ้นในของคุณ


3
ฉันคิดในบางสิ่งเช่นนี้ในตอนแรกโดยไม่มี maxTries ขอบคุณสำหรับคำตอบ!
Andres Farias

6
@AndresFarias .. ใช่จุดที่สำคัญที่สุดในคำตอบนี้คือการ maxTriesได้แก่ มิฉะนั้นมันจะทำงานinfinite loopหากผู้ใช้ป้อนข้อมูลผิดและจะไม่ออก คุณยินดีต้อนรับ :)
Rohit Jain

ขอบคุณสำหรับสิ่งนี้ - มันช่วยฉันไม่ให้ต้องเขียนโค้ดที่น่ารำคาญมาก ๆ !
David Holiday

2
เป็นไปได้หรือไม่ที่จะเพิ่มฟังก์ชั่น Thread.sleep () ภายในตัวจับที่นี่ เพราะในบางกรณีเช่นรอการตอบสนองหน้าในห้องสมุดซีลีเนียมที่มีความสำคัญ ขอบคุณ
Suat Atan ปริญญาเอก

2
ใช้งานได้ดี! สำหรับผู้เริ่มต้น: หากคุณได้รับการวนซ้ำไม่สิ้นสุดให้ตรวจสอบว่าคุณเพิ่ม "break;" ในตอนท้ายของบล็อก "ลอง"
Krzysztof Walczewski

59

วิธีแก้ปัญหา "enterprisy":

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

และโทร:

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

6
คุณควรโยนข้อยกเว้นอีกครั้งหากการทดลองซ้ำครั้งล่าสุดล้มเหลวตามที่ทำไว้ในคำตอบอื่น ๆ ที่ได้รับ
cvacca

35

ตามปกติการออกแบบที่ดีที่สุดขึ้นอยู่กับสถานการณ์เฉพาะ โดยปกติแล้วฉันจะเขียนสิ่งที่ชอบ:

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

รอทำไมจึงไม่มีเงื่อนไขภายในสำหรับการประกาศลูปเช่น: สำหรับ (int retries = 0; retries <6; retries ++) ??
Didier A.

8
เพราะฉันเพียงต้องการที่จะโยนในความพยายามครั้งสุดท้ายและดังนั้นบล็อก catch ต้องการเงื่อนไขนั้นทำให้เงื่อนไขในการซ้ำซ้อน
meriton

1
ฉันไม่คิดว่าcontinueจำเป็นต้องมี .. และคุณสามารถพลิกเงื่อนไขได้
Koray Tugay

19

แม้ว่าtry/catchเป็นwhileกลยุทธ์ที่รู้จักกันดีและดีฉันต้องการแนะนำให้คุณโทรซ้ำ:

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
การเรียกซ้ำดีกว่าลูปสำหรับกรณีการใช้งานนี้อย่างไร
ด่าน

7
การติดตามสแต็กอาจดูแปลก ๆ เล็กน้อยในอันนี้เพราะจะไม่limitนับวิธีที่ถูกเรียกซ้ำ ตรงข้ามกับเวอร์ชันลูปซึ่งจะโยนในระดับ 'ดั้งเดิม' ...
Clockwork-Muse

7
แน่นอนว่าดูสวยบนกระดาษ แต่ฉันไม่แน่ใจว่าการเรียกซ้ำเป็นวิธีการที่เหมาะสม
โทมัส

3
ฉันไม่เข้าใจว่าทำไมการเรียกซ้ำที่นี่ด้วย อย่างไรก็ตามฉันคิดว่ามันง่ายที่จะ:void retry(int times) { (...) if (times==0) throw w; retry(times--);
sinuhepop

8
เป็นวิธีปฏิบัติที่ไม่ดีที่จะใช้การเรียกซ้ำเพื่อทดแทนการวนซ้ำเพียงอย่างเดียว การเรียกซ้ำใช้สำหรับเมื่อคุณต้องการผลักดันและดึงข้อมูลบางอย่างออกมา
มาร์ควิสแห่ง Lorne

19

สถานการณ์ที่แน่นอนของคุณจัดการผ่านFailsafe :

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

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

ค่อนข้างง่าย


5
ห้องสมุดที่ดีมาก
Maksim

สำหรับผู้ที่สงสัยคุณจะต้องใช้สิ่งนี้ในการพึ่งพาไล่ระดับสีของคุณ - คอมไพล์ 'net.jodah: failafe: 1.1.0'
Shreyas

18

คุณสามารถใช้คำอธิบายประกอบ AOP และ Java จากjcabi-problems (ฉันเป็นผู้พัฒนา):

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

คุณสามารถใช้@Loggableและใส่@LogExceptionคำอธิบายประกอบ


ว้าว ! ฟังดูแฟนซี! :)
Alind Billore

ควรเป็นคำตอบที่ดีที่สุด
Mohamed Taher Alrefaie

2
มีวิธีการ "แก้ไข" ข้อผิดพลาดเมื่อพยายามล้มเหลว (อุปการะบางอย่างที่อาจแก้ไขความพยายามต่อไป)? ดูคำถาม: fix_the_problem();ใน catch block
warch

เมื่อพิจารณาถึงจำนวนของปัญหาที่เปิดอยู่และเวลาที่ผ่านไปสำหรับข้อบกพร่องที่ยอมรับแล้วซึ่งไม่ได้รับการแก้ไขฉันจะไม่พึ่งพาไลบรารีนี้
Michael Lihs

6

คำตอบเหล่านี้ส่วนใหญ่เหมือนกันหมด Mine เป็นเช่นนั้น แต่นี่เป็นรูปแบบที่ฉันชอบ

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

หนึ่งในข้อเสียเปรียบคือคุณยังโทรfix_the_problemหลังจากความพยายามครั้งสุดท้าย นั่นอาจเป็นการดำเนินการที่มีค่าใช้จ่ายสูงและอาจเสียเวลา
โจอาคิมซาวเออร์

2
@JoachimSauer True คุณทำได้if (tryCount < max) fix()- แต่นี่เป็นรูปแบบของวิธีการทั่วไป รายละเอียดจะขึ้นอยู่กับกรณีที่เฉพาะเจาะจง นอกจากนี้ยังมีRetasherจากฝรั่งที่ฉันเคยดู
สตีเฟ่น P

4

Spring AOP และโซลูชันที่ใช้คำอธิบายประกอบ:

การใช้งาน ( @RetryOperationคือคำอธิบายประกอบที่กำหนดเองของเราสำหรับงาน):

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

เราจะต้องมีสองสิ่งในการทำสิ่งนี้ให้สำเร็จ: 1. อินเทอร์เฟซคำอธิบายประกอบและ 2. ด้านสปริง นี่เป็นวิธีหนึ่งในการใช้สิ่งเหล่านี้:

อินเทอร์เฟซคำอธิบายประกอบ:

import java.lang.annotation.*;

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

มุมมองฤดูใบไม้ผลิ:

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

ใช้การwhileวนซ้ำด้วยการstatusตั้งค่าสถานะ เริ่มต้นการตั้งค่าสถานะเป็นfalseและตั้งค่าtrueเมื่อการดำเนินการสำเร็จเช่นด้านล่าง:

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

การดำเนินการนี้จะพยายามอีกครั้งจนกว่าจะประสบความสำเร็จ

หากคุณต้องการลองใหม่จำนวนครั้งเท่านั้นให้ใช้ตัวนับเช่นกัน:

  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
  }

การดำเนินการนี้จะพยายามสูงสุด 10 ครั้งหากไม่ประสบความสำเร็จจนกว่าจะถึงเวลานั้นจะจบการทำงานหากประสบความสำเร็จก่อนมือ


แทนที่จะตรวจสอบ!successในขณะที่คุณคุณสามารถแบ่งออกในขณะที่ความสำเร็จเป็นจริง
Rohit Jain

1
@RohitJain: มันดูสะอาดกว่าสำหรับฉัน
Yogendra Singh

@YogendraSingh .. แปลก ในขณะที่คุณจะไม่ได้รับการปรับเปลี่ยนของคุณได้ทุกที่ในของคุณsuccess ดังนั้นดูเหมือนว่าซ้ำซ้อนในการตรวจสอบสำหรับมันในการทำงานของทุกcatch catch
Rohit Jain

@RohitJain: Catch เป็นเพียงการแก้ไขข้อมูล มันจะกลับไปและเรียกใช้คำสั่งอีกครั้ง successหากประสบความสำเร็จก็จะปรับเปลี่ยน ลองดู
Yogendra Singh

3

นี่เป็นคำถามเก่า แต่โซลูชันยังคงมีความเกี่ยวข้อง นี่คือโซลูชันทั่วไปของฉันใน Java 8 โดยไม่ต้องใช้ห้องสมุดบุคคลที่สาม:

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

มีกรณีทดสอบเช่น:

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

ความคิดที่ดีผู้สร้างไปๆมาๆ!
HankTheTank

2

วิธีง่ายๆในการแก้ไขปัญหานี้คือการลอง / จับในขณะที่วนซ้ำและรักษาจำนวนไว้ วิธีนี้คุณสามารถป้องกันการวนซ้ำไม่สิ้นสุดโดยตรวจสอบการนับกับตัวแปรอื่น ๆ ในขณะที่ยังคงบันทึกการทำงานของความล้มเหลวของคุณ มันไม่ใช่ทางออกที่ยอดเยี่ยมที่สุด แต่จะได้ผล


1

ใช้สิ่งที่ต้องทำขณะออกแบบบล็อกลองใหม่

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

2
รหัสควรโยนข้อยกเว้นเดิมหากไม่สำเร็จ
lilalinux

1

ในกรณีที่มีประโยชน์ตัวเลือกเพิ่มเติมในการพิจารณาทั้งหมดถูกรวมเข้าด้วยกัน (stopfile แทนการลองใหม่นอนหลับวนรอบต่อที่มีขนาดใหญ่ขึ้น) อาจเป็นประโยชน์ทั้งหมด

 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
}

ผู้ลงคะแนนโปรดแสดงความคิดเห็นว่าทำไมขอบคุณ!
rogerdpack

1
นี่คือความไม่รู้ที่แท้จริงในการ downvote และไม่อ้างถึงเหตุผล
xploreraj

การนอนหลับไม่ชัดเจนตั้งแต่ในขณะที่วงจะไม่รอ
João Pimentel Ferreira

1

คุณสามารถใช้ได้ https://github.com/bnsd55/RetryCatch

ตัวอย่าง:

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

แทนที่จะเป็นnew ExampleRunnable()คุณสามารถส่งผ่านฟังก์ชั่นนิรนามของคุณเอง


1

หากไม่ใช่ข้อยกเว้นทั้งหมดรับประกันลองใหม่มีเพียงบางส่วนเท่านั้น และหากต้องลองอย่างน้อยหนึ่งครั้งต่อไปนี้เป็นวิธีอรรถประโยชน์อื่น:

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

การใช้งาน:

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

0

Try-Catch ทั้งหมดไม่อนุญาตให้โปรแกรมของคุณล้มเหลว โดยทั่วไปแล้วคุณจะพยายามบันทึกข้อผิดพลาดและอาจย้อนกลับการเปลี่ยนแปลงหากคุณต้องการ

bool finished = false;

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

คุณหมายถึงเป็นนอกคอก(!finished)หรือ
แซมฉันว่า Reinstate Monica

1
@RohitJain while(finished)มันดูมากเกินไปเช่น ฉันชอบที่จะใช้เวอร์ชัน verbose มากขึ้น
Sam ฉันว่า Reinstate Monica

3
while(!finished)หน้าตาเป็นอย่างไรบ้างwhile (finished)?
Rohit Jain

@Rohit เพราะมันมีตัวละครเพียงตัวเดียวที่แตกต่างกัน พวกเขาทั้งหมดได้รวบรวมลงในสิ่งเดียวกัน ใน C # ฉันใช้วิธีการขยายสตริงIsPopulated()ซึ่งเพิ่งกลับ!IsNullOrEmpty()มาเพื่อให้แน่ใจว่านักพัฒนาทั้งหมดเข้าใจเจตนาของฉัน
Michael Blackburn

0

ฉันรู้ว่ามีคำตอบที่คล้ายกันอยู่แล้วที่นี่และของฉันก็ไม่ได้แตกต่างกันมากนัก แต่ฉันจะโพสต์ไว้เพราะมันเกี่ยวข้องกับกรณี / ปัญหาเฉพาะ

เมื่อจัดการกับสิ่งที่facebook Graph APIอยู่ภายในPHPคุณบางครั้งได้รับข้อผิดพลาด แต่การลองอีกครั้งโดยทันทีในสิ่งเดียวกันจะให้ผลลัพธ์ที่เป็นบวก (สำหรับเหตุผลทางอินเทอร์เน็ตที่มีมนต์ขลังหลายประการ ในกรณีนี้ไม่จำเป็นต้องแก้ไขข้อผิดพลาดใด ๆ แต่ให้ลองอีกครั้งเพราะมี "facebook error" บางประเภท

รหัสนี้ใช้ทันทีหลังจากสร้างเฟส 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(); }
    }
}

นอกจากนี้การมีการforนับลูปนับเป็นศูนย์ ( $attempt--) ทำให้ง่ายต่อการเปลี่ยนจำนวนครั้งในอนาคต


0

ต่อไปนี้เป็นวิธีแก้ปัญหาของฉันด้วยวิธีที่ง่ายมาก!

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

                    }
                }

1
โปรดดูคำตอบ @Rohit Jain ซึ่งเฉพาะเจาะจงมากขึ้นและไม่วนซ้ำไม่สิ้นสุดในกรณีลบ
จันทรา Shekhar

0

ฉันไม่แน่ใจว่านี่เป็นวิธี "ระดับมืออาชีพ" หรือไม่และฉันไม่แน่ใจว่าจะใช้กับทุกสิ่งได้หรือไม่

boolean gotError = false;

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


0

ต่อไปนี้เป็นวิธีการใช้ซ้ำและทั่วไปสำหรับ Java 8+ ที่ไม่ต้องการไลบรารีภายนอก:

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

การใช้งาน:

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

0

ปัญหาของการแก้ปัญหาที่เหลืออยู่ก็คือฟังก์ชั่นผู้ติดต่อจะพยายามอย่างต่อเนื่องโดยไม่มีช่วงเวลาในระหว่างนั้น

ทำไมไม่ลองแค่tryทุกวินาทีและโฆษณาชั่วนิรันดร์ ?

นี่คือวิธีใช้setTimeoutและฟังก์ชั่นวนซ้ำ:

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

แทนที่ฟังก์ชั่นRun()ด้วยฟังก์ชั่นหรือรหัสที่คุณต้องการใช้ใหม่tryทุกวินาที


0

ให้มันลองใช้สปริง @Retryable คำอธิบายประกอบวิธีการด้านล่างจะลองอีกครั้ง 3 ครั้งเมื่อ RuntimeException เกิดขึ้น

@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

ตัวอย่างด้านล่างเรียกใช้โค้ดขนาดสั้น หากคุณได้รับข้อผิดพลาดใด ๆ ในขณะที่เรียกใช้งานข้อมูลโค้ดให้เข้าสู่โหมดสลีปเป็น M มิลลิวินาทีและลองอีกครั้ง การอ้างอิงการเชื่อมโยง

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

นี่คือวิธีการแก้ปัญหาของฉันคล้ายกับที่คนอื่น ๆ สามารถห่อฟังก์ชั่น แต่ช่วยให้คุณได้รับค่าตอบแทนฟังก์ชั่นถ้ามันเกิน

    /**
     * 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");
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.