一种退避序列实现

编程入门 行业动态 更新时间:2024-10-13 08:25:41

一种退避<a href=https://www.elefans.com/category/jswz/34/1769864.html style=序列实现"/>

一种退避序列实现

介绍功能和需求背景
介绍代码上下文,思路,方案。 
讲解代码,评委穿插提问。参会的其他同学有问题也可以提。 
评委对代码给建议和反馈

写在前面

         你有一个苹果,我有一个苹果,交换一下我们还是只有一个苹果;你有一本书,我有一本书,交换一下我们就有两本书了。每个程序员都有自己的工具集和代码私库,如果大家一起贡献出来,我们就会有一套较完整的工具集,相信可以解决大部分问题。

         作为一个程序员,一个工科生,现实中的理想就是有一套趁手的完整的工具套件,家里有点什么事情,只要拿出我的工具套件就能自己轻松搞定。跟现实中一样,我们中间件团队也在默默的打造自己的软件工具套件,也希望更多的同学一起来打造这一套称心的软件工具套件。

一、需求背景

1.1 业务需求

1.1.1 Nebulax流程实例保存失败重试和告警

流程执行完后会将流程实例调用http接口上报到星云服务端,若星云服务端不可用,不会发起重试,导致流程虽然执行完成,但是在控制台查看不到对应流程实例信息。- 改造成上报失败按(指数退避算法)策略,在一段时间后重试,确保流程实例能够上传到服务端
- 若上传失败,在尝试若干次后记录日志并发送告警给对应业务负责人

1.1.2 模拟MetaQ失败退避算法

网关应用原先依赖MetaQ,改造升级后内部的重试策略也是继承一致的重试策略,按固定级别(1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h)退避。

1.1.3 延迟调度平台重试策略优化

  • 发送消息时设置重试策略,目前支持两种重试策略

1、【普通模式】:默认模式,强烈建议用该模式,该模式间隔会逐步递增(5s,10s,30s,60s... 逐渐递增,总周期为7天,总共20次)。

2、【表达式模式】:

距离上次重试的时间间隔 = 首次重试间隔 + 已重试次数 * 重试时间间隔步长 * 首次重试间隔(秒)

在表达式模式的策略下,最大重试、首次重试间隔和步长才会生效。注意普通模式下,以下参数不生效。

  • 调度限流重试策略

  1. 主动限流使用随机数,5~15分钟

jobDetail.setRedoIntervalMode(RedoIntervalModeConstant.EXPRESSION);
// 最大重试次数,默认99次 
jobDetail.setFailRedoCountMax(10);
// 首次重试间隔(秒)
jobDetail.setFailFirstRedoInterval(5);
// 重试时间间隔步长,如设置成0,则每次重试间隔相同,如设置成1,则间隔是5、10、15...
jobDetail.setFailRedoIntervalStep(0);

1.2 需求整理

  • 根据策略获取下一次重试间隔

  • 根据策略获取指定次数的重试间隔

  • 多种退避算法,如指数退避、固定步长、等差数列、自定义固定级别退避,以及不退避。

  • 可以设置最大重试间隔,如最大重试间隔不能超过1小时

  • 可以设置最大重试次数,如最大重试20次

  • 可以获取当前重试次数,即已经执行了几次

  • 可以获取从开始到当前流逝的全部时间

  • 可以获取从开始到指定次数流逝的全部时间

  • 标准化重试策略和接口,方便延迟调度平台使用

二 方案调研

2.1 Guava实现

<dependency><groupId>com.github.rholder</groupId><artifactId>guava-retrying</artifactId><version>1.0.6</version>
</dependency>

详见: guava-retrying by rholder

2.2 Spring实现

<dependency> <groupId>org.springframework</groupId>  <artifactId>spring-core</artifactId> <version>4.3.17.RELEASE<version>
</dependency> 

public interface BackOff {/*** Start a new back off execution.* @return a fresh {@link BackOffExecution} ready to be used*/BackOffExecution start();}
package org.springframework.util.backoff;/*** Represent a particular back-off execution.** <p>Implementations do not need to be thread safe.** @author Stephane Nicoll* @since 4.1* @see BackOff*/
public interface BackOffExecution {/*** Return value of {@link #nextBackOff()} that indicates that the operation* should not be retried.*/long STOP = -1;/*** Return the number of milliseconds to wait before retrying the operation* or {@link #STOP} ({@value #STOP}) to indicate that no further attempt* should be made for the operation.*/long nextBackOff();}

三、我们的方案

2.2.1 基于Spring实现扩展

package com.xxxmons.retry.backoff;/*** @created 2021-12-20 4:26 PM* @description:*/
public interface Backoff {/*** 开始** @return*/BackoffExecution start();}
package com.xxxmons.retry.backoff;import java.util.concurrent.TimeUnit;/*** @created 2021-12-20 4:26 PM* @description:*/
public interface BackoffExecution {/*** 不再重试时返回的值*/long STOP = -1;/*** The default time unit*/TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS;/*** 下一次退避时间** @return nextBackoff 下一次重试延迟时间(毫秒),不再重试时返回{@link #STOP} ({@value #STOP})*/long nextBackoff();/*** 根据重试次数获取指定次数的退避时间** @param attempt* @return*/long getBackoff(int attempt);/*** 从开始到现在一共经过的时间** @return*/long getElapsedTime();/*** 从开始到指定次数一共经过的时间** @param attempt* @return*/long getElapsedTime(int attempt);/*** 当前尝试次数** @return currentAttempt*/int currentAttempt();}

2.2.2 代码清单

接口与类

功能说明

备注说明

com.xxxmons.retry.RetryService

重试服务工具类

com.xxxmons.retry.backoff.Backoff

退避算法定义接口

com.xxxmons.retry.backoff.BackoffExecution

退避算法执行接口

com.xxxmons.retry.backoff.BackoffType

退避算法类型接口

com.xxxmons.retry.backoff.BackoffTypeEnum

退避算法类型枚举类

com.xxxmons.retry.backoff.CustomBackoff

自定义序列退避算法实现

com.xxxmons.retry.backoff.ExponentialBackoff

指数退避算法实现

com.xxxmons.retry.backoff.FixedBackoff

固定步长退避算法实现

com.xxxmons.retry.backoff.GradualBackoff

等差数列退避算法实现

com.xxxmons.retry.backoff.NoneBackoff

不退避算法实现

com.xxxmons.retry.backoff.FibonacciBackoff

裴波那契退避算法实现

暂未实现

package com.xxxmons.retry.backoff;/*** @created 2021-12-31 3:24 PM* @description:*/
public interface BackoffType {/*** 类型编码** @return*/int getCode();/*** 类型名字** @return*/String getName();/*** 类型说明** @return*/String getDesc();
}
package com.xxxmons.retry.backoff;/*** 重试补偿步长类型** @created 2021-12-20 4:29 PM* @description:*/
public enum BackoffTypeEnum implements BackoffType {/*** 无补偿**/NONE(0, "none", "无补偿"),/*** 固定步长**/FIXED(1, "fixed", "固定步长"),/*** 等差步长*/GRADUAL(2, "gradual", "等差步长"),/*** 指数退避**/EXPONENTIAL(3, "exponential", "指数退避"),/*** 自定义步长*/CUSTOM(4, "custom", "自定义步长"),;private int code;private String name;private String desc;BackoffTypeEnum(int code, String name, String desc) {this.code = code;this.name = name;this.desc = desc;}public static BackoffTypeEnum typeOf(int code) {for (BackoffTypeEnum type : BackoffTypeEnum.values()) {if (type.code == code) {return type;}}return null;}@Overridepublic int getCode() {return code;}@Overridepublic String getName() {return name;}@Overridepublic String getDesc() {return desc;}
}
package com.xxxmons.retry;/*** @created 2021-12-31 3:21 PM* @description:*/import com.xxxmons.retry.backoff.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;public class RetryService {static {Runtime.getRuntime().addShutdownHook(RetryServiceShutdownHook.instance());}/*** 重试线程池大小*/public static final int RETRY_CORE_POOL_SIZE = Integer.getInteger(RetryConstants.DEFAULT_RETRY_SERVICE_SCHEDULED_THREAD_POOL_SIZE_KEY,RetryConstants.DEFAULT_RETRY_SERVICE_SCHEDULED_THREAD_POOL_DEFAULT_SIZE);/*** 重试服务线程池*/final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(RETRY_CORE_POOL_SIZE);private static final RetryService instance = new RetryService();public static RetryService getInstance() {return instance;}private RetryService() {RetryServiceShutdownHook.instance().addExecutor(executor);}public ScheduledFuture<?> retry(Callable callable, long delay) {return executor.schedule(callable, delay, BackoffExecution.DEFAULT_TIME_UNIT);}public ScheduledFuture<?> retry(Runnable runnable, long delay) {return executor.schedule(runnable, delay, BackoffExecution.DEFAULT_TIME_UNIT);}public ScheduledFuture<?> retry(Callable callable, long delay, TimeUnit unit) {return executor.schedule(callable, delay, unit);}public ScheduledFuture<?> retry(Runnable runnable, long delay, TimeUnit unit) {return executor.schedule(runnable, delay, unit);}public interface RetryConstants {String DEFAULT_RETRY_SERVICE_SCHEDULED_THREAD_POOL_SIZE_KEY = "default.retry.service.scheduled.thread.pool.size";int DEFAULT_RETRY_SERVICE_SCHEDULED_THREAD_POOL_DEFAULT_SIZE = 8;}static class RetryServiceShutdownHook extends Thread {private final Logger logger = LoggerFactory.getLogger(this.getClass());private List<ExecutorService> executors = Collections.synchronizedList(new ArrayList<ExecutorService>());private final static RetryServiceShutdownHook instance = new RetryServiceShutdownHook();public static RetryServiceShutdownHook instance() {return instance;}private RetryServiceShutdownHook() {}public RetryServiceShutdownHook addExecutor(ExecutorService executor) {executors.add(executor);return this;}@Overridepublic void run() {logger.info("retry service shutdown");for (int i = 0; i < executors.size(); i++) {ExecutorService executorService = executors.get(i);try {if (!executorService.isShutdown()) {executorService.shutdown();}} catch (Exception e) {logger.error("retry service shutdown executor service error", e.getMessage());}}}}}

package com.xxxmons.retry.backoff;import com.xxxmons.data.constant.CommonConstants;import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;/*** @created 2021-12-27 9:47 AM* @description: 自定义序列退避算法实现*/
public class CustomBackoff implements Backoff, Serializable {private static final long serialVersionUID = 5677908306014056187L;public static final String DEFAULT_SEPARATOR = CommonConstants.SPACE_SEPARATOR;public static final String LEFT_BRACKET = "{";public static final String RIGHT_BRACKET = "}";/*** Constant value indicating an unlimited number of attempt.*/public static final int UNLIMITED_ATTEMPTS = Integer.MAX_VALUE;/*** 默认退避时间序列* 示例1:1  2  3  10  10  10  30  60 120 180 600 1800 3600  3600  3600  3600  3600  3600* 示例2:1s 2s 3s 10s 10s 10s 30s 1m 2m  3m  10m 30m  1h    1h    1h    1h    1h    1h* 示例3:1  2  3  10{3}       30  60 120 180 600 1800 3600{6}* 示例4:1s 2s 3s 10s{3}      30s 1m 2m  3m  10m 30m  1h{6}*/public static final String DEFAULT_SEQUENCE = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h{64}";/*** 自定义退避序列*/private String sequence = DEFAULT_SEQUENCE;/*** 最大尝试次数*/private int maxAttempt = UNLIMITED_ATTEMPTS;/*** 自定义序列分隔符*/private String separator = DEFAULT_SEPARATOR;/*** Create an instance with an sequence with DEFAULT.*/public CustomBackoff() {}/*** Create an instance.** @param sequence the origin sequence*/public CustomBackoff(String sequence) {this.sequence = sequence;}public CustomBackoff(String sequence, String separator) {this.sequence = sequence;this.separator = separator;}public CustomBackoff(int maxAttempt) {this.maxAttempt = maxAttempt;}public CustomBackoff(String sequence, int maxAttempt) {this.sequence = sequence;this.maxAttempt = maxAttempt;}/*** @param sequence   the origin sequence* @param maxAttempt the maximum number of attempt* @param separator  the sequence's separator*/public CustomBackoff(String sequence, int maxAttempt, String separator) {this.sequence = sequence;this.maxAttempt = maxAttempt;this.separator = separator;}/*** Set the maximum number of attempt in milliseconds.*/public void setMaxAttempt(int maxAttempt) {this.maxAttempt = maxAttempt;}/*** Return the maximum number of attempt in milliseconds.*/public int getMaxAttempt() {return maxAttempt;}public String getSeparator() {return separator;}public void setSeparator(String separator) {this.separator = separator;}@Overridepublic BackoffExecution start() {return new CustomBackoffExecution(this.sequence, this.maxAttempt, this.separator);}public static class CustomBackoffExecution implements BackoffExecution, Serializable {private static final long serialVersionUID = 2991013977130023823L;/*** 自定义退避序列*/private String sequence;/*** 最大尝试次数*/private int maxAttempt;/*** 自定义序列分隔符*/private String separator;/*** 解析后的退避时间间隔列表*/private List<Long> intervals;private long currentElapsedTime = 0;private int currentAttempt = 0;public CustomBackoffExecution() {this(DEFAULT_SEQUENCE, UNLIMITED_ATTEMPTS, DEFAULT_SEPARATOR);}public CustomBackoffExecution(String sequence, int maxAttempt, String separator) {this.sequence = sequence;this.maxAttempt = maxAttempt;this.separator = separator;this.parse();}private void parse() {intervals = new ArrayList<>();HashMap<String, Long> timeUnitTable = new HashMap<String, Long>();timeUnitTable.put("s", 1000L);timeUnitTable.put("m", 1000L * 60);timeUnitTable.put("h", 1000L * 60 * 60);timeUnitTable.put("d", 1000L * 60 * 60 * 24);String[] fields = sequence.split(separator);boolean completed = false;for (String field : fields) {field = field.trim();int count = 1;long multiple = 1;int leftBracket = field.indexOf(CustomBackoff.LEFT_BRACKET);int rightBracket = field.indexOf(CustomBackoff.RIGHT_BRACKET);if (leftBracket > 0 && rightBracket > 0) {count = Integer.parseInt(field.substring(leftBracket + 1, rightBracket));field = field.substring(0, leftBracket);}Long unit = timeUnitTable.get(String.valueOf(field.charAt(field.length() - 1)));if (unit != null) {multiple = unit.longValue();field = field.substring(0, field.length() - 1);}long timeInMills = Long.parseLong(field) * multiple;for (int i = 0; i < count; i++) {if (this.intervals.size() < this.maxAttempt) {this.intervals.add(timeInMills);} else {completed = true;}}if (completed) {break;}}// if maxAttempt greater than intervals's size, then set intervals's size as maxAttemptif (this.intervals.size() < this.maxAttempt) {this.maxAttempt = this.intervals.size();}}@Overridepublic int currentAttempt() {return currentAttempt;}@Overridepublic long getElapsedTime() {return this.currentElapsedTime;}@Overridepublic long nextBackoff() {if (currentAttempt >= maxAttempt) {return STOP;}if (currentAttempt >= this.intervals.size()) {return STOP;}long nextInterval = this.intervals.get(this.currentAttempt);this.currentElapsedTime += nextInterval;this.currentAttempt += 1;return nextInterval;}@Overridepublic void updateCurrentAttempt(int attempt) {if (attempt < 0) {this.currentAttempt = 0;} else if (attempt >= maxAttempt) {this.currentAttempt = maxAttempt;} else {this.currentAttempt = attempt;}this.currentElapsedTime = this.getElapsedTime(this.currentAttempt);}@Overridepublic long getBackoff(int attempt) {if (attempt <= 0) {return 0L;}if (attempt - 1 >= maxAttempt) {return STOP;}if (attempt > this.intervals.size()) {return STOP;}return this.intervals.get(attempt - 1);}@Overridepublic long getElapsedTime(int attempt) {if (attempt >= maxAttempt) {attempt = maxAttempt;}if (attempt >= this.intervals.size()) {attempt = this.intervals.size();}long elapsedTime = 0;for (int i = 0; i < attempt; i++) {elapsedTime += this.intervals.get(i);}return elapsedTime;}public String getSequence() {return sequence;}public void setSequence(String sequence) {this.sequence = sequence;}@Overridepublic int getMaxAttempt() {return maxAttempt;}@Overridepublic void setMaxAttempt(int maxAttempt) {this.maxAttempt = maxAttempt;}public String getSeparator() {return separator;}public void setSeparator(String separator) {this.separator = separator;}public List<Long> getIntervals() {return intervals;}public void setIntervals(List<Long> intervals) {this.intervals = intervals;}public long getCurrentElapsedTime() {return currentElapsedTime;}public void setCurrentElapsedTime(long currentElapsedTime) {this.currentElapsedTime = currentElapsedTime;}public int getCurrentAttempt() {return currentAttempt;}public void setCurrentAttempt(int currentAttempt) {this.currentAttempt = currentAttempt;}@Overridepublic String toString() {final StringBuilder sb = new StringBuilder("CustomBackoff{");sb.append("intervals=").append(intervals);String attemptValue = (maxAttempt == Integer.MAX_VALUE ?"unlimited" : String.valueOf(maxAttempt));sb.append(", maxAttempt=").append(attemptValue);sb.append(", currentElapsedTime=").append(currentElapsedTime);sb.append(", currentAttempt=").append(this.currentAttempt);sb.append('}');return sb.toString();}}public static void main(String[] args) {BackoffExecution backoffExecution = new CustomBackoff().start();System.out.println(backoffExecution.getBackoff(1));System.out.println(backoffExecution.getBackoff(2));System.out.println(backoffExecution.getBackoff(3));System.out.println(backoffExecution.getElapsedTime(1));System.out.println(backoffExecution.getElapsedTime(2));System.out.println(backoffExecution.getElapsedTime(3));}}

package com.xxxmons.retry.backoff;import java.io.Serializable;
import java.util.concurrent.TimeUnit;/*** @created 2021-12-20 4:31 PM* @description:*/
public class ExponentialBackoff implements Backoff, Serializable {private static final long serialVersionUID = -3955367506662250971L;/*** The default initial interval.*/public static final long DEFAULT_INITIAL_INTERVAL = 2000L;/*** The default multiplier (increases the interval by 50%).*/public static final double DEFAULT_MULTIPLIER = 2;/*** The default maximum back off time.*/public static final long DEFAULT_MAX_INTERVAL = 10 * 60 * 1000L;/*** Constant value indicating an unlimited number of attempt.*/public static final int UNLIMITED_ATTEMPTS = Integer.MAX_VALUE;private long initialInterval = DEFAULT_INITIAL_INTERVAL;private double multiplier = DEFAULT_MULTIPLIER;private long maxInterval = DEFAULT_MAX_INTERVAL;private int maxAttempt = UNLIMITED_ATTEMPTS;/*** Create an instance with the default settings.** @see #DEFAULT_INITIAL_INTERVAL* @see #DEFAULT_MULTIPLIER* @see #DEFAULT_MAX_INTERVAL*/public ExponentialBackoff() {}/*** Create an instance.** @param maxAttempt the maximum number of attempt*/public ExponentialBackoff(int maxAttempt) {this.maxAttempt = maxAttempt;}/*** Create an instance with the supplied settings.** @param initialInterval the initial interval in milliseconds*/public ExponentialBackoff(long initialInterval) {this.initialInterval = initialInterval;this.checkInterval(this.initialInterval, this.maxInterval);}/*** Create an instance with the supplied settings.** @param initialInterval the initial interval* @param timeUnit        the initial interval's time unit* @param multiplier      the multiplier (should be greater than or equal to 1)*/public ExponentialBackoff(long initialInterval, TimeUnit timeUnit, double multiplier) {checkMultiplier(multiplier);this.multiplier = multiplier;this.initialInterval = BackoffExecution.DEFAULT_TIME_UNIT.convert(initialInterval, timeUnit);this.checkInterval(this.initialInterval, this.maxInterval);}public ExponentialBackoff(long initialInterval, long maxInterval, TimeUnit timeUnit, double multiplier) {checkMultiplier(multiplier);this.multiplier = multiplier;this.initialInterval = BackoffExecution.DEFAULT_TIME_UNIT.convert(initialInterval, timeUnit);this.maxInterval = BackoffExecution.DEFAULT_TIME_UNIT.convert(maxInterval, timeUnit);this.checkInterval(this.initialInterval, this.maxInterval);}/*** The initial interval in milliseconds.*/public void setInitialInterval(long initialInterval) {this.initialInterval = initialInterval;this.checkInterval(this.initialInterval, this.maxInterval);}/*** Return the initial interval in milliseconds.*/public long getInitialInterval() {return initialInterval;}/*** The value to multiply the current interval by for each retry attempt.*/public void setMultiplier(double multiplier) {checkMultiplier(multiplier);this.multiplier = multiplier;}/*** Return the value to multiply the current interval by for each retry attempt.*/public double getMultiplier() {return multiplier;}/*** The maximum back off time.*/public void setMaxInterval(long maxInterval) {this.maxInterval = maxInterval;}/*** Return the maximum back off time.*/public long getMaxInterval() {return maxInterval;}/*** Set the maximum number of attempt in milliseconds.*/public void setMaxAttempt(int maxAttempt) {this.maxAttempt = maxAttempt;}/*** Return the maximum number of attempt in milliseconds.*/public int getMaxAttempt() {return maxAttempt;}@Overridepublic BackoffExecution start() {return new ExponentialBackoffExecution(initialInterval, maxInterval, multiplier, maxAttempt);}private void checkMultiplier(double multiplier) {if (multiplier < 1) {throw new IllegalArgumentException("Invalid multiplier '" + multiplier + "'. Should be equal" +"or higher than 1. A multiplier of 1 is equivalent to a fixed interval");}}private void checkInterval(long initialInterval, long maxInterval) {if (initialInterval <= 0) {throw new IllegalArgumentException("Invalid initialInterval '" + initialInterval + "'. Should be higher" +" than 0. A initialInterval of 0 is equivalent to no interval");}if (maxInterval <= initialInterval * getMultiplier()) {throw new IllegalArgumentException("Invalid maxInterval '" + maxInterval + "'. Should be higher" +" than initialInterval * multiplier. A maxInterval equal or less than initialInterval * multiplier " +"is equivalent to a fixed interval");}}public static class ExponentialBackoffExecution implements BackoffExecution, Serializable {private static final long serialVersionUID = 672748521748221970L;private long initialInterval;private double multiplier;private long maxInterval;private int maxAttempt;private long currentInterval = -1;private long currentElapsedTime = 0;private int currentAttempt = 0;public ExponentialBackoffExecution() {this(DEFAULT_INITIAL_INTERVAL, DEFAULT_MAX_INTERVAL, DEFAULT_MULTIPLIER, UNLIMITED_ATTEMPTS);}public ExponentialBackoffExecution(long initialInterval, long maxInterval, double multiplier, int maxAttempt) {this.initialInterval = initialInterval;this.maxInterval = maxInterval;this.multiplier = multiplier;this.maxAttempt = maxAttempt;}@Overridepublic int currentAttempt() {return currentAttempt;}@Overridepublic long getElapsedTime() {return this.currentElapsedTime;}@Overridepublic long nextBackoff() {if (currentAttempt >= maxAttempt) {return STOP;}long nextInterval = computeNextInterval();this.currentElapsedTime += nextInterval;this.currentAttempt += 1;return nextInterval;}private long computeNextInterval() {long maxInterval = this.maxInterval;if (this.currentInterval >= maxInterval) {return maxInterval;} else if (this.currentInterval < 0) {this.currentInterval = this.initialInterval;} else {this.currentInterval = multiplyInterval(this.currentInterval, maxInterval);}return this.currentInterval;}private long multiplyInterval(long currentInterval, long maxInterval) {long interval = currentInterval;interval *= this.multiplier;return (interval > maxInterval ? maxInterval : interval);}@Overridepublic void updateCurrentAttempt(int attempt) {if (attempt < 0) {this.currentAttempt = 0;} else if (attempt >= maxAttempt) {this.currentAttempt = maxAttempt;} else {this.currentAttempt = attempt;}this.currentElapsedTime = this.getElapsedTime(this.currentAttempt);}@Overridepublic long getBackoff(int attempt) {if (attempt <= 0) {return 0L;}if (attempt - 1 >= maxAttempt) {return STOP;}long interval = this.initialInterval;long maxInterval = this.maxInterval;for (long i = 1; i < attempt; i++) {interval = multiplyInterval(interval, maxInterval);if (interval >= maxInterval) {break;}}return interval;}@Overridepublic long getElapsedTime(int attempt) {if (attempt >= maxAttempt) {attempt = maxAttempt;}long interval = this.initialInterval;long maxInterval = this.maxInterval;long elapsedTime = 0;if (attempt <= 0) {return elapsedTime;} else {elapsedTime = interval;}for (long i = 1; i < attempt; i++) {if (interval < maxInterval) {interval = multiplyInterval(interval, maxInterval);}elapsedTime += interval;}return elapsedTime;}public long getInitialInterval() {return initialInterval;}public void setInitialInterval(long initialInterval) {this.initialInterval = initialInterval;}public double getMultiplier() {return multiplier;}public void setMultiplier(double multiplier) {this.multiplier = multiplier;}public long getMaxInterval() {return maxInterval;}public void setMaxInterval(long maxInterval) {this.maxInterval = maxInterval;}@Overridepublic int getMaxAttempt() {return maxAttempt;}@Overridepublic void setMaxAttempt(int maxAttempt) {this.maxAttempt = maxAttempt;}public long getCurrentInterval() {return currentInterval;}public void setCurrentInterval(long currentInterval) {this.currentInterval = currentInterval;}public long getCurrentElapsedTime() {return currentElapsedTime;}public void setCurrentElapsedTime(long currentElapsedTime) {this.currentElapsedTime = currentElapsedTime;}public int getCurrentAttempt() {return currentAttempt;}public void setCurrentAttempt(int currentAttempt) {this.currentAttempt = currentAttempt;}@Overridepublic String toString() {StringBuilder sb = new StringBuilder("ExponentialBackOff{");sb.append("currentInterval=").append(this.currentInterval < 0 ? "n/a" : this.currentInterval + "ms");sb.append(", multiplier=").append(this.multiplier);sb.append('}');return sb.toString();}}}
package com.xxxmons.retry.backoff;import java.io.Serializable;
import java.util.concurrent.TimeUnit;/*** @created 2021-12-20 4:32 PM* @description:*/
public class FixedBackoff implements Backoff, Serializable {private static final long serialVersionUID = 2511140811757045231L;/*** The default recovery interval: 5000 ms = 5 seconds.*/public static final long DEFAULT_INTERVAL = 5000L;/*** Constant value indicating an unlimited number of attempt.*/public static final int UNLIMITED_ATTEMPTS = Integer.MAX_VALUE;private long interval = DEFAULT_INTERVAL;private int maxAttempt = UNLIMITED_ATTEMPTS;/*** Create an instance with an interval of {@value #DEFAULT_INTERVAL}* ms and an unlimited number of attempt.*/public FixedBackoff() {}/*** Create an instance.** @param maxAttempt the maximum number of attempt*/public FixedBackoff(int maxAttempt) {this.maxAttempt = maxAttempt;}/*** Create an instance.** @param interval the interval between two attempt* @param timeUnit the time unit of interval*/public FixedBackoff(long interval, TimeUnit timeUnit) {this.interval = BackoffExecution.DEFAULT_TIME_UNIT.convert(interval, timeUnit);this.maxAttempt = maxAttempt;}/*** Create an instance.** @param maxAttempt the maximum number of attempt* @param interval   the interval between two attempt*/public FixedBackoff(int maxAttempt, long interval) {this.interval = interval;this.maxAttempt = maxAttempt;}/*** Create an instance.** @param maxAttempt the maximum number of attempt* @param interval   the interval between two attempt* @param timeUnit   the time unit of interval*/public FixedBackoff(int maxAttempt, long interval, TimeUnit timeUnit) {this.interval = BackoffExecution.DEFAULT_TIME_UNIT.convert(interval, timeUnit);this.maxAttempt = maxAttempt;}/*** Set the interval between two attempt in milliseconds.*/public void setInterval(long interval) {this.interval = interval;}/*** Return the interval between two attempt in milliseconds.*/public long getInterval() {return interval;}/*** Set the maximum number of attempt in milliseconds.*/public void setMaxAttempt(int maxAttempt) {this.maxAttempt = maxAttempt;}/*** Return the maximum number of attempt in milliseconds.*/public int getMaxAttempt() {return maxAttempt;}@Overridepublic BackoffExecution start() {return new FixedBackoffExecution(interval, maxAttempt);}public static class FixedBackoffExecution implements BackoffExecution, Serializable {private static final long serialVersionUID = 1656175761226107038L;private long interval;private int maxAttempt;private int currentAttempt = 0;private long currentElapsedTime = 0;public FixedBackoffExecution() {this(DEFAULT_INTERVAL, UNLIMITED_ATTEMPTS);}public FixedBackoffExecution(long interval, int maxAttempt) {this.interval = interval;this.maxAttempt = maxAttempt;}public long getInterval() {return interval;}public void setInterval(long interval) {this.interval = interval;}@Overridepublic int getMaxAttempt() {return maxAttempt;}@Overridepublic void setMaxAttempt(int maxAttempt) {this.maxAttempt = maxAttempt;}public int getCurrentAttempt() {return currentAttempt;}public void setCurrentAttempt(int currentAttempt) {this.currentAttempt = currentAttempt;}public long getCurrentElapsedTime() {return currentElapsedTime;}public void setCurrentElapsedTime(long currentElapsedTime) {this.currentElapsedTime = currentElapsedTime;}@Overridepublic int currentAttempt() {return currentAttempt;}@Overridepublic long getElapsedTime() {return this.currentElapsedTime;}@Overridepublic long nextBackoff() {if (this.currentAttempt < this.maxAttempt) {long nextInterval = this.interval;this.currentElapsedTime += nextInterval;this.currentAttempt++;return nextInterval;} else {return STOP;}}@Overridepublic void updateCurrentAttempt(int attempt) {if (attempt < 0) {this.currentAttempt = 0;} else if (attempt >= maxAttempt) {this.currentAttempt = maxAttempt;} else {this.currentAttempt = attempt;}this.currentElapsedTime = this.getElapsedTime(this.currentAttempt);}@Overridepublic long getBackoff(int attempt) {if (attempt <= 0) {return 0L;}if (attempt - 1 >= maxAttempt) {return STOP;}return this.interval;}@Overridepublic long getElapsedTime(int attempt) {return this.interval * attempt;}@Overridepublic String toString() {final StringBuilder sb = new StringBuilder("FixedBackoff{");sb.append("interval=").append(this.interval);String attemptValue = (this.maxAttempt == Integer.MAX_VALUE ?"unlimited" : String.valueOf(this.maxAttempt));sb.append(", maxAttempt=").append(attemptValue);sb.append(", currentAttempt=").append(this.currentAttempt);sb.append('}');return sb.toString();}}}
package com.xxxmons.retry.backoff;import java.io.Serializable;
import java.util.concurrent.TimeUnit;import static com.xxxmons.retry.backoff.BackoffExecution.DEFAULT_TIME_UNIT;/*** @created 2021-12-20 4:33 PM* @description:*/
public class GradualBackoff implements Backoff, Serializable {private static final long serialVersionUID = -4082336395861849848L;/*** The default initial interval.*/public static final long DEFAULT_INITIAL_INTERVAL = 2000L;/*** The default recovery interval: 3000 ms = 3 seconds.*/public static final long DEFAULT_INTERVAL = 3000L;/*** Constant value indicating an unlimited number of attempt.*/public static final int UNLIMITED_ATTEMPTS = Integer.MAX_VALUE;private long initialInterval = DEFAULT_INITIAL_INTERVAL;private long interval = DEFAULT_INTERVAL;private int maxAttempt = UNLIMITED_ATTEMPTS;/*** Create an instance with an interval of {@value #DEFAULT_INTERVAL}* ms and an unlimited number of attempt.*/public GradualBackoff() {}/*** Create an instance.** @param maxAttempt the maximum number of attempt*/public GradualBackoff(int maxAttempt) {this.maxAttempt = maxAttempt;}/*** Create an instance.** @param interval the interval between two attempt* @param timeUnit the time unit of interval*/public GradualBackoff(long initialInterval, long interval, TimeUnit timeUnit) {this.initialInterval = DEFAULT_TIME_UNIT.convert(initialInterval, timeUnit);this.interval = DEFAULT_TIME_UNIT.convert(interval, timeUnit);this.maxAttempt = maxAttempt;}/*** Create an instance.** @param maxAttempt the maximum number of attempt* @param interval   the interval between two attempt*/public GradualBackoff(int maxAttempt, long initialInterval, long interval) {this.initialInterval = initialInterval;this.interval = interval;this.maxAttempt = maxAttempt;}/*** Create an instance.** @param maxAttempt the maximum number of attempt* @param interval   the interval between two attempt* @param timeUnit   the time unit of interval*/public GradualBackoff(int maxAttempt, long initialInterval, long interval, TimeUnit timeUnit) {this.initialInterval = DEFAULT_TIME_UNIT.convert(initialInterval, timeUnit);this.interval = DEFAULT_TIME_UNIT.convert(interval, timeUnit);this.maxAttempt = maxAttempt;}/*** Return the interval of first attempt in milliseconds.** @return*/public long getInitialInterval() {return initialInterval;}/*** Set the interval of first attempt in milliseconds.*/public void setInitialInterval(long initialInterval) {this.initialInterval = initialInterval;}/*** Set the interval between two attempt in milliseconds.*/public void setInterval(long interval) {this.interval = interval;}/*** Return the interval between two attempt in milliseconds.*/public long getInterval() {return interval;}/*** Set the maximum number of attempt in milliseconds.*/public void setMaxAttempt(int maxAttempt) {this.maxAttempt = maxAttempt;}/*** Return the maximum number of attempt in milliseconds.*/public int getMaxAttempt() {return maxAttempt;}@Overridepublic BackoffExecution start() {return new GradualBackoffExecution(initialInterval, interval, maxAttempt);}public static class GradualBackoffExecution implements BackoffExecution, Serializable {private static final long serialVersionUID = 6691539566098442985L;private long initialInterval;private long interval;private int maxAttempt;private int currentAttempt = 0;private long currentElapsedTime = 0;public GradualBackoffExecution() {this(DEFAULT_INITIAL_INTERVAL, DEFAULT_INTERVAL, UNLIMITED_ATTEMPTS);}public GradualBackoffExecution(long initialInterval, long interval, int maxAttempt) {this.initialInterval = initialInterval;this.interval = interval;this.maxAttempt = maxAttempt;}@Overridepublic int currentAttempt() {return currentAttempt;}@Overridepublic long getElapsedTime() {return this.currentElapsedTime;}@Overridepublic long nextBackoff() {if (this.currentAttempt < this.maxAttempt) {long nextInterval = this.currentAttempt == 0 ? this.initialInterval : this.interval;this.currentElapsedTime += nextInterval;this.currentAttempt++;return nextInterval;} else {return STOP;}}@Overridepublic void updateCurrentAttempt(int attempt) {if (attempt < 0) {this.currentAttempt = 0;} else if (attempt >= maxAttempt) {this.currentAttempt = maxAttempt;} else {this.currentAttempt = attempt;}this.currentElapsedTime = this.getElapsedTime(this.currentAttempt);}@Overridepublic long getBackoff(int attempt) {if (attempt <= 0) {return 0L;}if (attempt - 1 >= maxAttempt) {return STOP;}return attempt == 1 ? this.initialInterval : this.interval;}@Overridepublic long getElapsedTime(int attempt) {return attempt == 1 ? this.initialInterval : this.initialInterval + this.interval * (attempt - 1);}public long getInitialInterval() {return initialInterval;}public void setInitialInterval(long initialInterval) {this.initialInterval = initialInterval;}public long getInterval() {return interval;}public void setInterval(long interval) {this.interval = interval;}@Overridepublic int getMaxAttempt() {return maxAttempt;}@Overridepublic void setMaxAttempt(int maxAttempt) {this.maxAttempt = maxAttempt;}public int getCurrentAttempt() {return currentAttempt;}public void setCurrentAttempt(int currentAttempt) {this.currentAttempt = currentAttempt;}public long getCurrentElapsedTime() {return currentElapsedTime;}public void setCurrentElapsedTime(long currentElapsedTime) {this.currentElapsedTime = currentElapsedTime;}@Overridepublic String toString() {final StringBuilder sb = new StringBuilder("GradualBackoff{");sb.append("initialInterval=").append(this.initialInterval);sb.append("interval=").append(this.interval);String attemptValue = (this.maxAttempt == Integer.MAX_VALUE ?"unlimited" : String.valueOf(this.maxAttempt));sb.append(", maxAttempt=").append(attemptValue);sb.append(", currentAttempt=").append(this.currentAttempt);sb.append('}');return sb.toString();}}}
package com.xxxmons.retry.backoff;import java.io.Serializable;/*** @created 2021-12-20 4:34 PM* @description:*/
public class NoneBackoff implements Backoff, Serializable {private static final long serialVersionUID = 8227959838869099892L;/*** The default recovery interval: 0 ms = 0 seconds.*/public static final long DEFAULT_INTERVAL = 0L;/*** Constant value indicating an unlimited number of attempt.*/public static final int UNLIMITED_ATTEMPTS = Integer.MAX_VALUE;private long interval = DEFAULT_INTERVAL;private int maxAttempt = UNLIMITED_ATTEMPTS;/*** Create an instance with an interval of {@value #DEFAULT_INTERVAL}* ms and an unlimited number of attempt.*/public NoneBackoff() {}/*** Create an instance.** @param maxAttempt the maximum number of attempt*/public NoneBackoff(int maxAttempt) {this.maxAttempt = maxAttempt;}/*** Return the interval between two attempt in milliseconds.*/public long getInterval() {return interval;}/*** Return the maximum number of attempt in milliseconds.*/public int getMaxAttempt() {return maxAttempt;}/*** Set the maximum number of attempt in milliseconds.*/public void setMaxAttempt(int maxAttempt) {this.maxAttempt = maxAttempt;}@Overridepublic BackoffExecution start() {return new NoneBackoffExecution(interval, maxAttempt);}public static class NoneBackoffExecution implements BackoffExecution, Serializable {private static final long serialVersionUID = 2453613900451888865L;private long interval;private int maxAttempt;private int currentAttempt = 0;private long currentElapsedTime = 0;public NoneBackoffExecution() {this(DEFAULT_INTERVAL, UNLIMITED_ATTEMPTS);}public NoneBackoffExecution(long interval, int maxAttempt) {this.interval = interval;this.maxAttempt = maxAttempt;}@Overridepublic int currentAttempt() {return currentAttempt;}@Overridepublic long getElapsedTime() {return this.currentElapsedTime;}@Overridepublic long nextBackoff() {if (this.currentAttempt < this.maxAttempt) {long nextInterval = this.interval;this.currentElapsedTime += nextInterval;this.currentAttempt++;return nextInterval;} else {return STOP;}}@Overridepublic void updateCurrentAttempt(int attempt) {if (attempt < 0) {this.currentAttempt = 0;} else if (attempt >= maxAttempt) {this.currentAttempt = maxAttempt;} else {this.currentAttempt = attempt;}this.currentElapsedTime = this.getElapsedTime(this.currentAttempt);}@Overridepublic long getBackoff(int attempt) {if (attempt <= 0) {return 0L;}if (attempt - 1 >= maxAttempt) {return STOP;}return this.interval;}@Overridepublic long getElapsedTime(int attempt) {return this.interval * attempt;}public long getInterval() {return interval;}public void setInterval(long interval) {this.interval = interval;}@Overridepublic int getMaxAttempt() {return maxAttempt;}@Overridepublic void setMaxAttempt(int maxAttempt) {this.maxAttempt = maxAttempt;}public int getCurrentAttempt() {return currentAttempt;}public void setCurrentAttempt(int currentAttempt) {this.currentAttempt = currentAttempt;}public long getCurrentElapsedTime() {return currentElapsedTime;}public void setCurrentElapsedTime(long currentElapsedTime) {this.currentElapsedTime = currentElapsedTime;}@Overridepublic String toString() {final StringBuilder sb = new StringBuilder("NoneBackoff{");sb.append("interval=").append(this.interval);sb.append(", maxAttempt=").append(this.maxAttempt);sb.append('}');return sb.toString();}}
}

2.2.3 业务使用示例

网关使用示例:

  • 添加依赖

    <!-- 公共模块 --><dependency><groupId>com.xxx.arch</groupId><artifactId>xxx-commons-recipes</artifactId><version>1.0.8</version> <!-- <version>${xxxmons.version}</version> --></dependency>
  • 初始化

@Configuration
@AutoConfigureAfter(OnsConfig.class)
public class LinkBean {public static final String MESSAGE_DELAY_LEVEL = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";@Beanpublic BackoffExecution backoffExecution() {return new CustomBackoff(MESSAGE_DELAY_LEVEL, MAX_RETRY_COUNT).start();}...
}
  • 具体使用

@Slf4j
public class RetryFilterFactory implements FilterFactory {private Producer producer;private BackoffExecution backoffExecution;public RetryFilterFactory(Producer producer, BackoffExecution backoffExecution) {this.producer = producer;this.backoffExecution = backoffExecution;}@Overridepublic String name() {return "retry";}@Overridepublic Filter apply(Properties properties) {return (context, chain) -> {int retry = context.getRetryCount();retry++;long retryTime = context.getAttributeOrDefault(LinkContext.FAILOVER_RETRY_TIME_ATTRIBUTE, 0L);context.getAttributes().put(LinkContext.FAILOVER_RETRY_TIME_ATTRIBUTE,retryTime + backoffExecution.getBackoff(retry));try {producer.send(context);} catch (Exception exp) {log.error("master send context: {}, error:", context, exp);throw new LinkIOException("send master mq error");}chain.filter(context);};}
}

四、未来展望

希望大家一起来共建xxx-commons组件,也希望xxx-commons组件能够成为内部基础组件被广泛使用,提升系统和业务开发效率,并提升开发质量。

更多推荐

一种退避序列实现

本文发布于:2023-12-07 09:20:40,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1670820.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:序列

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!