乐趣区

关于java:curator里的重试

curator 中如果连贯失败或获取数据失败是能够重试的,其中重试有从次数、工夫维度重试,这里重点提一下指数弥补重试。相干类:

public class ExponentialBackoffRetry extends SleepingRetry{
    private static final int MAX_RETRY_LIMIT = 29;
    private static final int DEFAULT_MAX_SLEEP_MS = Integer.MAX_VALUE;
    private final Random random = new Random();
    private final  int baseSleepTimeMs;
    private final  int maxSleepMs;
    public ExponentialBackoffRetry(int baseSleepTimeMs,int maxRetries){this(baseSleepTimeMs,maxRetries,DEFAULT_MAX_SLEEP_MS);
    }
    public ExponentialBackoffRetry(int baseSleepTimeMs,int maxRetries,int maxSleepMs){super(validateMaxRetries(maxRetries));
        this.baseSleepTimeMs = baseSleepTimeMs;
        this.maxSleepMs = maxSleepMs;
    }
    public long getBaseSleepTimeMs(){return baseSleepTimeMs;}


    private static int validateMaxRetries(int maxRetries){if(maxRetries > MAX_RETRY_LIMIT){maxRetries = MAX_RETRY_LIMIT;}
        return maxRetries;
    }
    @Override
    protected long getSleepTimeMs(int retryCount, long elapsedTimeMs) {
        // 这里应用了指数弥补或指数退却的思维
        // 应用随机,是为了避免出现惊群效应,避免雷同工夫执行大量操作
        //1 << n -> 2 的 n 次方;n << 1 -> 2n
        long sleepMs = baseSleepTimeMs * Math.max(1,random.nextInt(1 << (retryCount + 1)));
        if(sleepMs > maxSleepMs){sleepMs = maxSleepMs;}
        return sleepMs;
    }
}

重点在获取休眠工夫的办法 getSleepTimeMs 里,接着看看办法里的 retryCount 是如何传入的,这里波及到另一个类:RetryLoopImpl,代码如下:

/**
 * 对 RetryLoop 的实现,外面有 RetryPolicy、RetrySleeper 两个重要的属性
 * RetryPolicy 类里有通过对异样、重试次数来判断是否再进行重试
 * RetrySleeper 线程休眠工具
 */
public class RetryLoopImpl extends RetryLoop {

    private boolean isDone = false;
    private int retryCount = 0;
    private final long startTimeMs = System.currentTimeMillis();
    private final RetryPolicy retryPolicy;
    private static final RetrySleeper sleeper = (time, unit) -> unit.sleep(time);

    RetryLoopImpl(RetryPolicy retryPolicy){this.retryPolicy = retryPolicy;}

    static RetrySleeper getRetrySleeper(){return sleeper;}

    @Override
    public boolean shouldContinue() {return !isDone;}

    @Override
    public void markComplete() {isDone = true;}

    /**
     * 异样抛出后流程会中断
     * @param exception
     * @throws Exception
     */
    @Override
    public void takeException(Exception exception) throws Exception {
        boolean rethrow =true;
        if(retryPolicy.allowRetry(exception)){// 有的异样产生后是能够重试的
            // 在 sleep 肯定工夫内如果未被中断也能够重试
            if(retryPolicy.allowRetry(retryCount++,System.currentTimeMillis() - startTimeMs,sleeper)){rethrow = false;}
        }
        // 如果下面两个条件都不满足,则抛出异样完结流程
        if(rethrow){throw exception;}
    }
}

能够看到 retryCount 在 takeException 办法中会自增,所以在 ExponentialBackoffRetry 的 getSleepTimeMs 办法中通过左移来达到绝对指数增长的成果,这里说绝对是因为还应用了 random 来做一个随机,为了避免出现惊群效应。

 那重试的入口又在哪里呢?在 RetryLoop 这个抽象类里,代码如下:
/**
 * 有工作须要执行时,并不是间接去执行而是应用 RetryLoop 里的 callWithRetry 办法封装后再执行,* 这样,如果在执行的过程中产生异样,那么就能够依据异样的品种判断是持续重试还是抛出异样
 */
public abstract class RetryLoop {public static <T> T callWithRetry(RetryLoop retryLoop, Callable<T> proc)throws Exception{
        T result = null;
        while (retryLoop.shouldContinue()){
            try {result = proc.call();
                retryLoop.markComplete();}catch (Exception e){retryLoop.takeException(e);
            }
        }
        return result;
    }

    /**
     * 返回 true 须要继续执行
     * @return
     */
    public abstract boolean shouldContinue();

    /**
     * 工作执行胜利后调用这个办法
     */
    public abstract void markComplete();

    /**
     * pass any caught exception here,* 对异样进行过滤判断,从而进行重试或抛出异常中断流程
     * @param exception
     */
    public abstract void takeException(Exception exception) throws Exception;

}

咱们须要关注 callWithRetry 办法,办法承受一个重试策略和一个线程体,while 条件判断是否持续重试,如果办法执行胜利则标记不必重试而完结,如果在重试的过程中产生了异样,咱们传入的也是指数弥补重试策略那获取的休眠工夫也就是类指数增长了

退出移动版