All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.joyqueue.toolkit.retry.RetryPolicy Maven / Gradle / Ivy

/**
 * Copyright 2019 The JoyQueue Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.joyqueue.toolkit.retry;

import java.io.Serializable;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 重试策略
 */
public class RetryPolicy implements Serializable {
    private static final long serialVersionUID = 1L;
    public static final int EXPIRE_TIME = 1000 * 3600 * 24 * 3;
    public static final int RETRY_DELAY = 1000;
    public static final int MAX_RETRY_DELAY = 0;
    public static final double BACKOFF_MULTIPLIER = 2.0;
    public static final int MAX_RETRYS = 0;
    // 最大指数,初始化计算一次
    private volatile AtomicReference maxExponential = new AtomicReference();
    // 最大重试次数
    private Integer maxRetrys;
    // 最大重试间隔
    private Integer maxRetryDelay;
    // 重试间隔
    private Integer retryDelay;
    // 指数增加间隔时间
    private Boolean useExponentialBackOff;
    // 指数系数,必须>=1
    private Double backOffMultiplier;
    // 过期时间(默认3天)
    private Integer expireTime;

    public RetryPolicy() {
    }

    public RetryPolicy(Integer retryDelay, Integer maxRetrys) {
        setRetryDelay(retryDelay);
        setMaxRetryDelay(retryDelay);
        setMaxRetrys(maxRetrys);
    }

    public RetryPolicy(Integer retryDelay, Integer maxRetryDelay, Integer maxRetrys, Boolean useExponentialBackOff,
            Double backOffMultiplier, Integer expireTime) {
        setRetryDelay(retryDelay);
        setMaxRetryDelay(maxRetryDelay);
        setMaxRetrys(maxRetrys);
        setUseExponentialBackOff(useExponentialBackOff);
        setBackOffMultiplier(backOffMultiplier);
        setExpireTime(expireTime);
    }

    /**
     * 获取重试时间
     *
     * @param now        当前时间
     * @param retryTimes 当前重试次数,从1开始计数
     * @param startTime  初始化起始时间
     * @return  <=0 过期, >0 下次重试时间
     *
     */
    public long getTime(final long now, final int retryTimes, final long startTime) {
        long time = 0;
        int retrys = retryTimes < 1 ? 1 : retryTimes;
        int maxRetrys = this.maxRetrys == null ? MAX_RETRYS : this.maxRetrys;
        int retryDelay = this.retryDelay == null ? RETRY_DELAY : this.retryDelay;
        int maxRetryDelay = this.maxRetryDelay == null ? MAX_RETRY_DELAY : this.maxRetryDelay;
        boolean useExponentialBackOff = this.useExponentialBackOff == null ? true : this.useExponentialBackOff;
        double backOffMultiplier =
                (this.backOffMultiplier == null || this.backOffMultiplier < 1) ? BACKOFF_MULTIPLIER : this
                        .backOffMultiplier;
        int expireTime = this.expireTime == null ? EXPIRE_TIME : this.expireTime;
        // 判断是否超过最大重试次数
        if (maxRetrys > 0 && retrys > maxRetrys) {
            time = 0;
        } else if (retryDelay <= 0) {
            // 没有时间间隔
            time = now;
        } else {
            long delay = 0;

            // 判断是否使用指数函数
            if (useExponentialBackOff) {
                // 指数
                int exponential = retrys - 1;
                // 底数为1
                if (backOffMultiplier == 1) {
                    delay = retryDelay;
                } else if (maxRetryDelay > 0) {
                    // 获取最大的指数
                    Integer maxExp = maxExponential.get();
                    // 还没用计算过
                    if (maxExp == null) {
                        maxExp = (int) (Math.log(maxRetryDelay) / Math.log(backOffMultiplier));
                        if (!maxExponential.compareAndSet(null, maxExp)) {
                            maxExp = maxExponential.get();
                        }
                    }
                    // 超过了最大指数
                    if (exponential >= maxExp) {
                        delay = maxRetryDelay;
                    } else {
                        delay = Math.round(retryDelay * Math.pow(backOffMultiplier, exponential));
                    }
                } else {
                    delay = Math.round(retryDelay * Math.pow(backOffMultiplier, exponential));
                }
            } else {
                delay = retryDelay;
            }
            if (delay <= 0) {
                time = now;
            } else if (maxRetryDelay > 0 && delay >= maxRetryDelay) {
                time = now + maxRetryDelay;
            } else {
                time = now + delay;
            }
        }
        // 有过期时间设置
        if (expireTime > 0 && time > 0 && time >= (startTime + expireTime)) {
            time = 0;
        }
        return time;
    }

    public Integer getMaxRetrys() {
        return maxRetrys;
    }

    public void setMaxRetrys(Integer maxRetrys) {
        this.maxRetrys = maxRetrys;
    }

    public Integer getMaxRetryDelay() {
        return maxRetryDelay;
    }

    public void setMaxRetryDelay(Integer maxRetryDelay) {
        this.maxRetryDelay = maxRetryDelay;
        this.maxExponential.set(null);
    }

    public Integer getRetryDelay() {
        return retryDelay;
    }

    public void setRetryDelay(Integer retryDelay) {
        this.retryDelay = retryDelay;
        this.maxExponential.set(null);
    }

    public Boolean getUseExponentialBackOff() {
        return useExponentialBackOff;
    }

    public void setUseExponentialBackOff(Boolean useExponentialBackOff) {
        this.useExponentialBackOff = useExponentialBackOff;
    }

    public Double getBackOffMultiplier() {
        return backOffMultiplier;
    }

    public void setBackOffMultiplier(Double backOffMultiplier) {
        this.backOffMultiplier = backOffMultiplier;
        this.maxExponential.set(null);
    }

    public Integer getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(Integer expireTime) {
        this.expireTime = expireTime;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        RetryPolicy that = (RetryPolicy) o;

        if (backOffMultiplier != null ? !backOffMultiplier
                .equals(that.backOffMultiplier) : that.backOffMultiplier != null) {
            return false;
        }
        if (expireTime != null ? !expireTime.equals(that.expireTime) : that.expireTime != null) {
            return false;
        }
        if (maxRetryDelay != null ? !maxRetryDelay.equals(that.maxRetryDelay) : that.maxRetryDelay != null) {
            return false;
        }
        if (maxRetrys != null ? !maxRetrys.equals(that.maxRetrys) : that.maxRetrys != null) {
            return false;
        }
        if (retryDelay != null ? !retryDelay.equals(that.retryDelay) : that.retryDelay != null) {
            return false;
        }
        return useExponentialBackOff != null ? useExponentialBackOff
                .equals(that.useExponentialBackOff) : that.useExponentialBackOff == null;
    }

    @Override
    public int hashCode() {
        int result = maxRetrys != null ? maxRetrys.hashCode() : 0;
        result = 31 * result + (maxRetryDelay != null ? maxRetryDelay.hashCode() : 0);
        result = 31 * result + (retryDelay != null ? retryDelay.hashCode() : 0);
        result = 31 * result + (useExponentialBackOff != null ? useExponentialBackOff.hashCode() : 0);
        result = 31 * result + (backOffMultiplier != null ? backOffMultiplier.hashCode() : 0);
        result = 31 * result + (expireTime != null ? expireTime.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("RetryPolicy{");
        sb.append("maxRetrys=").append(maxRetrys);
        sb.append(", maxRetryDelay=").append(maxRetryDelay);
        sb.append(", retryDelay=").append(retryDelay);
        sb.append(", useExponentialBackOff=").append(useExponentialBackOff);
        sb.append(", backOffMultiplier=").append(backOffMultiplier);
        sb.append(", expireTime=").append(expireTime);
        sb.append('}');
        return sb.toString();
    }

    public static class Builder {
        // 最大重试次数(无限制)
        private Integer maxRetrys;
        // 最大重试间隔(默认5分钟)
        private Integer maxRetryDelay;
        // 重试间隔
        private Integer retryDelay;
        // 指数增加间隔时间
        private Boolean useExponentialBackOff;
        // 指数系数,必须>=1
        private Double backOffMultiplier;
        // 过期时间(默认3天)
        private Integer expireTime;

        public static Builder build() {
            return new Builder();
        }

        public Builder maxRetrys(Integer maxRetrys) {
            this.maxRetrys = maxRetrys;
            return this;
        }

        public Builder maxRetryDelay(Integer maxRetryDelay) {
            this.maxRetryDelay = maxRetryDelay;
            return this;
        }

        public Builder retryDelay(Integer retryDelay) {
            this.retryDelay = retryDelay;
            return this;
        }

        public Builder useExponentialBackOff(Boolean useExponentialBackOff) {
            this.useExponentialBackOff = useExponentialBackOff;
            return this;
        }

        public Builder backOffMultiplier(Double backOffMultiplier) {
            this.backOffMultiplier = backOffMultiplier;
            return this;
        }

        public Builder expireTime(Integer expireTime) {
            this.expireTime = expireTime;
            return this;
        }

        public RetryPolicy create() {
            return new RetryPolicy(retryDelay, maxRetryDelay, maxRetrys, useExponentialBackOff, backOffMultiplier,
                    expireTime);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy