Correct posture of Java method retry

Posted by lachild on Tue, 03 Dec 2019 10:20:26 +0100

1, introduction

In business development, it is likely to be the case of retrying.

Retry is mainly used when the call fails, especially when dubbo related exception and network related exception occur.

The following is a simple encapsulation of this function, and then gives some relatively more open source code addresses.

 

2. Simple package

github address https://github.com/chujianyun/simple-retry

Downloadable operation, fork improvement, welcome to provide valuable comments, welcome to contribute code.

 

Encapsulation operation

package com.chujianyun.simpleretry;

/**
 * Operation interface method
 *
 * @author: Mingming Ruyue liuwangyanghudu@163.com
 * @date: 2019-04-05 02:09
 */
public interface Operation<T> {
    T execute() throws Exception;
}

Package retry

package com.chujianyun.simpleretry;

import com.chujianyun.simpleretry.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

/**
 * Operation encapsulation
 *
 * @author: Mingming Ruyue liuwangyanghudu@163.com
 * @date: 2019-04-05 02:09
 */
@Slf4j
public class OperationHelper {

    /**
     * Operation execution with retry
     *
     * @param operation       Actions performed
     * @param maxAttemptTimes max retries 
     * @param <T>             return type
     * @return Return value
     * @throws Exception
     */
    public static <T> T executeWithRetry(Operation<T> operation, int maxAttemptTimes) throws Exception {
        return executeWithRetry(operation, maxAttemptTimes, 0, null);
    }

    /**
     * Operation execution with retry and delay
     *
     * @param operation       Actions performed
     * @param maxAttemptTimes max retries 
     * @param timeDelay       delayed
     * @param timeUnit        Time unit
     * @param <T>             return type
     * @return Return value
     * @throws Exception
     */
    public static <T> T executeWithRetry(Operation<T> operation, int maxAttemptTimes, int timeDelay, TimeUnit timeUnit) throws Exception {

        if (maxAttemptTimes < 1) {
            throw new IllegalArgumentException("max attempt times must not less than one");
        }
        int count = 1;

        while (true) {
            try {
                return operation.execute();
            } catch (BusinessException businessException) {
                log.debug("OperationHelper#businessException", businessException);
                throw new BusinessException();
            } catch (Exception e) {
                log.debug("OperationHelper#executeWithRetry", e);
                //Cumulative
                count++;
                if (count > maxAttemptTimes) {
                    throw e;
                }
                // delayed
                if (timeDelay >= 0 && timeUnit != null) {
                    try {
                        log.debug("delayed{}Millisecond", timeUnit.toMillis(timeDelay));
                        timeUnit.sleep(timeDelay);
                    } catch (InterruptedException ex) {
                        //ignore
                    }
                }

                log.debug("The first{}Second try", count - 1);
            }
        }
    }


}

 

In case of business exception, there is no need to try again and throw it out directly.

When a non business exception is encountered and the maximum number of retries is not exceeded, retry continuously. If a delay is set, retry after a delay.

Test class

package com.chujianyun.simpleretry;

import com.chujianyun.simpleretry.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * Retry test
 *
 * @author: Mingming Ruyue liuwangyanghdu@163.com
 * @date: 2019-04-04 10:42
 */
@Slf4j
public class OperationHelperTest {


    @Test(expected = Exception.class)
    public void executeWithRetry_Exception() throws Exception {
        OperationHelper.executeWithRetry(() -> {
            throw new Exception();
        }, 3, 5, TimeUnit.SECONDS);
    }

    @Test(expected = BusinessException.class)
    public void executeWithRetry_BusinessException() throws Exception {
        OperationHelper.executeWithRetry(() -> {
            throw new BusinessException();
        }, 3, 5, TimeUnit.SECONDS);
    }

    @Test
    public void executeWithRetry() throws Exception {
        Integer result = OperationHelper.executeWithRetry(() -> {
            // If the random number is odd, the parameter is abnormal and will be retried
            Random random = new Random();
            int nextInt = random.nextInt(100);
            if ((nextInt & 1) == 1) {
                log.debug("Generated random number{}It is an odd number. Retry will be triggered after an error is reported", nextInt);
                throw new IllegalArgumentException();
            }
            return random.nextInt(5);
        }, 3, 5, TimeUnit.SECONDS);
        log.debug("Final return value{}", result);
    }

}

3. Other plans

 

https://github.com/rholder/guava-retrying

https://github.com/elennick/retry4j

 

Topics: github Java Lombok Dubbo