• 一种退避序列实现


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

    写在前面

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

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

    一、需求背景

    1.1 业务需求

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

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

    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分钟

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

    1.2 需求整理

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

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

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

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

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

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

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

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

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

    二 方案调研

    2.1 Guava实现

    1. <dependency>
    2. <groupId>com.github.rholder</groupId>
    3. <artifactId>guava-retrying</artifactId>
    4. <version>1.0.6</version>
    5. </dependency>

    详见: guava-retrying by rholder

    2.2 Spring实现

    1. <dependency>
    2. <groupId>org.springframework</groupId>
    3. <artifactId>spring-core</artifactId>
    4. <version>4.3.17.RELEASE<version>
    5. </dependency>

    1. public interface BackOff {
    2. /**
    3. * Start a new back off execution.
    4. * @return a fresh {@link BackOffExecution} ready to be used
    5. */
    6. BackOffExecution start();
    7. }
    1. package org.springframework.util.backoff;
    2. /**
    3. * Represent a particular back-off execution.
    4. *
    5. *

      Implementations do not need to be thread safe.

    6. *
    7. * @author Stephane Nicoll
    8. * @since 4.1
    9. * @see BackOff
    10. */
    11. public interface BackOffExecution {
    12. /**
    13. * Return value of {@link #nextBackOff()} that indicates that the operation
    14. * should not be retried.
    15. */
    16. long STOP = -1;
    17. /**
    18. * Return the number of milliseconds to wait before retrying the operation
    19. * or {@link #STOP} ({@value #STOP}) to indicate that no further attempt
    20. * should be made for the operation.
    21. */
    22. long nextBackOff();
    23. }

    三、我们的方案

    2.2.1 基于Spring实现扩展

    1. package com.xxx.commons.retry.backoff;
    2. /**
    3. * @created 2021-12-20 4:26 PM
    4. * @description:
    5. */
    6. public interface Backoff {
    7. /**
    8. * 开始
    9. *
    10. * @return
    11. */
    12. BackoffExecution start();
    13. }
    1. package com.xxx.commons.retry.backoff;
    2. import java.util.concurrent.TimeUnit;
    3. /**
    4. * @created 2021-12-20 4:26 PM
    5. * @description:
    6. */
    7. public interface BackoffExecution {
    8. /**
    9. * 不再重试时返回的值
    10. */
    11. long STOP = -1;
    12. /**
    13. * The default time unit
    14. */
    15. TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS;
    16. /**
    17. * 下一次退避时间
    18. *
    19. * @return nextBackoff 下一次重试延迟时间(毫秒),不再重试时返回{@link #STOP} ({@value #STOP})
    20. */
    21. long nextBackoff();
    22. /**
    23. * 根据重试次数获取指定次数的退避时间
    24. *
    25. * @param attempt
    26. * @return
    27. */
    28. long getBackoff(int attempt);
    29. /**
    30. * 从开始到现在一共经过的时间
    31. *
    32. * @return
    33. */
    34. long getElapsedTime();
    35. /**
    36. * 从开始到指定次数一共经过的时间
    37. *
    38. * @param attempt
    39. * @return
    40. */
    41. long getElapsedTime(int attempt);
    42. /**
    43. * 当前尝试次数
    44. *
    45. * @return currentAttempt
    46. */
    47. int currentAttempt();
    48. }

    2.2.2 代码清单

    接口与类

    功能说明

    备注说明

    com.xxx.commons.retry.RetryService

    重试服务工具类

    com.xxx.commons.retry.backoff.Backoff

    退避算法定义接口

    com.xxx.commons.retry.backoff.BackoffExecution

    退避算法执行接口

    com.xxx.commons.retry.backoff.BackoffType

    退避算法类型接口

    com.xxx.commons.retry.backoff.BackoffTypeEnum

    退避算法类型枚举类

    com.xxx.commons.retry.backoff.CustomBackoff

    自定义序列退避算法实现

    com.xxx.commons.retry.backoff.ExponentialBackoff

    指数退避算法实现

    com.xxx.commons.retry.backoff.FixedBackoff

    固定步长退避算法实现

    com.xxx.commons.retry.backoff.GradualBackoff

    等差数列退避算法实现

    com.xxx.commons.retry.backoff.NoneBackoff

    不退避算法实现

    com.xxx.commons.retry.backoff.FibonacciBackoff

    裴波那契退避算法实现

    暂未实现

    1. package com.xxx.commons.retry.backoff;
    2. /**
    3. * @created 2021-12-31 3:24 PM
    4. * @description:
    5. */
    6. public interface BackoffType {
    7. /**
    8. * 类型编码
    9. *
    10. * @return
    11. */
    12. int getCode();
    13. /**
    14. * 类型名字
    15. *
    16. * @return
    17. */
    18. String getName();
    19. /**
    20. * 类型说明
    21. *
    22. * @return
    23. */
    24. String getDesc();
    25. }
    1. package com.xxx.commons.retry.backoff;
    2. /**
    3. * 重试补偿步长类型
    4. *
    5. * @created 2021-12-20 4:29 PM
    6. * @description:
    7. */
    8. public enum BackoffTypeEnum implements BackoffType {
    9. /**
    10. * 无补偿
    11. **/
    12. NONE(0, "none", "无补偿"),
    13. /**
    14. * 固定步长
    15. **/
    16. FIXED(1, "fixed", "固定步长"),
    17. /**
    18. * 等差步长
    19. */
    20. GRADUAL(2, "gradual", "等差步长"),
    21. /**
    22. * 指数退避
    23. **/
    24. EXPONENTIAL(3, "exponential", "指数退避"),
    25. /**
    26. * 自定义步长
    27. */
    28. CUSTOM(4, "custom", "自定义步长"),
    29. ;
    30. private int code;
    31. private String name;
    32. private String desc;
    33. BackoffTypeEnum(int code, String name, String desc) {
    34. this.code = code;
    35. this.name = name;
    36. this.desc = desc;
    37. }
    38. public static BackoffTypeEnum typeOf(int code) {
    39. for (BackoffTypeEnum type : BackoffTypeEnum.values()) {
    40. if (type.code == code) {
    41. return type;
    42. }
    43. }
    44. return null;
    45. }
    46. @Override
    47. public int getCode() {
    48. return code;
    49. }
    50. @Override
    51. public String getName() {
    52. return name;
    53. }
    54. @Override
    55. public String getDesc() {
    56. return desc;
    57. }
    58. }
    1. package com.xxx.commons.retry;
    2. /**
    3. * @created 2021-12-31 3:21 PM
    4. * @description:
    5. */
    6. import com.xxx.commons.retry.backoff.*;
    7. import org.slf4j.Logger;
    8. import org.slf4j.LoggerFactory;
    9. import java.util.ArrayList;
    10. import java.util.Collections;
    11. import java.util.List;
    12. import java.util.concurrent.*;
    13. public class RetryService {
    14. static {
    15. Runtime.getRuntime().addShutdownHook(RetryServiceShutdownHook.instance());
    16. }
    17. /**
    18. * 重试线程池大小
    19. */
    20. public static final int RETRY_CORE_POOL_SIZE = Integer.getInteger(
    21. RetryConstants.DEFAULT_RETRY_SERVICE_SCHEDULED_THREAD_POOL_SIZE_KEY,
    22. RetryConstants.DEFAULT_RETRY_SERVICE_SCHEDULED_THREAD_POOL_DEFAULT_SIZE);
    23. /**
    24. * 重试服务线程池
    25. */
    26. final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(RETRY_CORE_POOL_SIZE);
    27. private static final RetryService instance = new RetryService();
    28. public static RetryService getInstance() {
    29. return instance;
    30. }
    31. private RetryService() {
    32. RetryServiceShutdownHook.instance().addExecutor(executor);
    33. }
    34. public ScheduledFuture retry(Callable callable, long delay) {
    35. return executor.schedule(callable, delay, BackoffExecution.DEFAULT_TIME_UNIT);
    36. }
    37. public ScheduledFuture retry(Runnable runnable, long delay) {
    38. return executor.schedule(runnable, delay, BackoffExecution.DEFAULT_TIME_UNIT);
    39. }
    40. public ScheduledFuture retry(Callable callable, long delay, TimeUnit unit) {
    41. return executor.schedule(callable, delay, unit);
    42. }
    43. public ScheduledFuture retry(Runnable runnable, long delay, TimeUnit unit) {
    44. return executor.schedule(runnable, delay, unit);
    45. }
    46. public interface RetryConstants {
    47. String DEFAULT_RETRY_SERVICE_SCHEDULED_THREAD_POOL_SIZE_KEY = "default.retry.service.scheduled.thread.pool.size";
    48. int DEFAULT_RETRY_SERVICE_SCHEDULED_THREAD_POOL_DEFAULT_SIZE = 8;
    49. }
    50. static class RetryServiceShutdownHook extends Thread {
    51. private final Logger logger = LoggerFactory.getLogger(this.getClass());
    52. private List executors = Collections.synchronizedList(new ArrayList());
    53. private final static RetryServiceShutdownHook instance = new RetryServiceShutdownHook();
    54. public static RetryServiceShutdownHook instance() {
    55. return instance;
    56. }
    57. private RetryServiceShutdownHook() {
    58. }
    59. public RetryServiceShutdownHook addExecutor(ExecutorService executor) {
    60. executors.add(executor);
    61. return this;
    62. }
    63. @Override
    64. public void run() {
    65. logger.info("retry service shutdown");
    66. for (int i = 0; i < executors.size(); i++) {
    67. ExecutorService executorService = executors.get(i);
    68. try {
    69. if (!executorService.isShutdown()) {
    70. executorService.shutdown();
    71. }
    72. } catch (Exception e) {
    73. logger.error("retry service shutdown executor service error", e.getMessage());
    74. }
    75. }
    76. }
    77. }
    78. }

    1. package com.xxx.commons.retry.backoff;
    2. import com.xxx.commons.data.constant.CommonConstants;
    3. import java.io.Serializable;
    4. import java.util.ArrayList;
    5. import java.util.HashMap;
    6. import java.util.List;
    7. /**
    8. * @created 2021-12-27 9:47 AM
    9. * @description: 自定义序列退避算法实现
    10. */
    11. public class CustomBackoff implements Backoff, Serializable {
    12. private static final long serialVersionUID = 5677908306014056187L;
    13. public static final String DEFAULT_SEPARATOR = CommonConstants.SPACE_SEPARATOR;
    14. public static final String LEFT_BRACKET = "{";
    15. public static final String RIGHT_BRACKET = "}";
    16. /**
    17. * Constant value indicating an unlimited number of attempt.
    18. */
    19. public static final int UNLIMITED_ATTEMPTS = Integer.MAX_VALUE;
    20. /**
    21. * 默认退避时间序列
    22. * 示例1:1 2 3 10 10 10 30 60 120 180 600 1800 3600 3600 3600 3600 3600 3600
    23. * 示例2:1s 2s 3s 10s 10s 10s 30s 1m 2m 3m 10m 30m 1h 1h 1h 1h 1h 1h
    24. * 示例3:1 2 3 10{3} 30 60 120 180 600 1800 3600{6}
    25. * 示例4:1s 2s 3s 10s{3} 30s 1m 2m 3m 10m 30m 1h{6}
    26. */
    27. 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}";
    28. /**
    29. * 自定义退避序列
    30. */
    31. private String sequence = DEFAULT_SEQUENCE;
    32. /**
    33. * 最大尝试次数
    34. */
    35. private int maxAttempt = UNLIMITED_ATTEMPTS;
    36. /**
    37. * 自定义序列分隔符
    38. */
    39. private String separator = DEFAULT_SEPARATOR;
    40. /**
    41. * Create an instance with an sequence with DEFAULT.
    42. */
    43. public CustomBackoff() {
    44. }
    45. /**
    46. * Create an instance.
    47. *
    48. * @param sequence the origin sequence
    49. */
    50. public CustomBackoff(String sequence) {
    51. this.sequence = sequence;
    52. }
    53. public CustomBackoff(String sequence, String separator) {
    54. this.sequence = sequence;
    55. this.separator = separator;
    56. }
    57. public CustomBackoff(int maxAttempt) {
    58. this.maxAttempt = maxAttempt;
    59. }
    60. public CustomBackoff(String sequence, int maxAttempt) {
    61. this.sequence = sequence;
    62. this.maxAttempt = maxAttempt;
    63. }
    64. /**
    65. * @param sequence the origin sequence
    66. * @param maxAttempt the maximum number of attempt
    67. * @param separator the sequence's separator
    68. */
    69. public CustomBackoff(String sequence, int maxAttempt, String separator) {
    70. this.sequence = sequence;
    71. this.maxAttempt = maxAttempt;
    72. this.separator = separator;
    73. }
    74. /**
    75. * Set the maximum number of attempt in milliseconds.
    76. */
    77. public void setMaxAttempt(int maxAttempt) {
    78. this.maxAttempt = maxAttempt;
    79. }
    80. /**
    81. * Return the maximum number of attempt in milliseconds.
    82. */
    83. public int getMaxAttempt() {
    84. return maxAttempt;
    85. }
    86. public String getSeparator() {
    87. return separator;
    88. }
    89. public void setSeparator(String separator) {
    90. this.separator = separator;
    91. }
    92. @Override
    93. public BackoffExecution start() {
    94. return new CustomBackoffExecution(this.sequence, this.maxAttempt, this.separator);
    95. }
    96. public static class CustomBackoffExecution implements BackoffExecution, Serializable {
    97. private static final long serialVersionUID = 2991013977130023823L;
    98. /**
    99. * 自定义退避序列
    100. */
    101. private String sequence;
    102. /**
    103. * 最大尝试次数
    104. */
    105. private int maxAttempt;
    106. /**
    107. * 自定义序列分隔符
    108. */
    109. private String separator;
    110. /**
    111. * 解析后的退避时间间隔列表
    112. */
    113. private List intervals;
    114. private long currentElapsedTime = 0;
    115. private int currentAttempt = 0;
    116. public CustomBackoffExecution() {
    117. this(DEFAULT_SEQUENCE, UNLIMITED_ATTEMPTS, DEFAULT_SEPARATOR);
    118. }
    119. public CustomBackoffExecution(String sequence, int maxAttempt, String separator) {
    120. this.sequence = sequence;
    121. this.maxAttempt = maxAttempt;
    122. this.separator = separator;
    123. this.parse();
    124. }
    125. private void parse() {
    126. intervals = new ArrayList<>();
    127. HashMap timeUnitTable = new HashMap();
    128. timeUnitTable.put("s", 1000L);
    129. timeUnitTable.put("m", 1000L * 60);
    130. timeUnitTable.put("h", 1000L * 60 * 60);
    131. timeUnitTable.put("d", 1000L * 60 * 60 * 24);
    132. String[] fields = sequence.split(separator);
    133. boolean completed = false;
    134. for (String field : fields) {
    135. field = field.trim();
    136. int count = 1;
    137. long multiple = 1;
    138. int leftBracket = field.indexOf(CustomBackoff.LEFT_BRACKET);
    139. int rightBracket = field.indexOf(CustomBackoff.RIGHT_BRACKET);
    140. if (leftBracket > 0 && rightBracket > 0) {
    141. count = Integer.parseInt(field.substring(leftBracket + 1, rightBracket));
    142. field = field.substring(0, leftBracket);
    143. }
    144. Long unit = timeUnitTable.get(String.valueOf(field.charAt(field.length() - 1)));
    145. if (unit != null) {
    146. multiple = unit.longValue();
    147. field = field.substring(0, field.length() - 1);
    148. }
    149. long timeInMills = Long.parseLong(field) * multiple;
    150. for (int i = 0; i < count; i++) {
    151. if (this.intervals.size() < this.maxAttempt) {
    152. this.intervals.add(timeInMills);
    153. } else {
    154. completed = true;
    155. }
    156. }
    157. if (completed) {
    158. break;
    159. }
    160. }
    161. // if maxAttempt greater than intervals's size, then set intervals's size as maxAttempt
    162. if (this.intervals.size() < this.maxAttempt) {
    163. this.maxAttempt = this.intervals.size();
    164. }
    165. }
    166. @Override
    167. public int currentAttempt() {
    168. return currentAttempt;
    169. }
    170. @Override
    171. public long getElapsedTime() {
    172. return this.currentElapsedTime;
    173. }
    174. @Override
    175. public long nextBackoff() {
    176. if (currentAttempt >= maxAttempt) {
    177. return STOP;
    178. }
    179. if (currentAttempt >= this.intervals.size()) {
    180. return STOP;
    181. }
    182. long nextInterval = this.intervals.get(this.currentAttempt);
    183. this.currentElapsedTime += nextInterval;
    184. this.currentAttempt += 1;
    185. return nextInterval;
    186. }
    187. @Override
    188. public void updateCurrentAttempt(int attempt) {
    189. if (attempt < 0) {
    190. this.currentAttempt = 0;
    191. } else if (attempt >= maxAttempt) {
    192. this.currentAttempt = maxAttempt;
    193. } else {
    194. this.currentAttempt = attempt;
    195. }
    196. this.currentElapsedTime = this.getElapsedTime(this.currentAttempt);
    197. }
    198. @Override
    199. public long getBackoff(int attempt) {
    200. if (attempt <= 0) {
    201. return 0L;
    202. }
    203. if (attempt - 1 >= maxAttempt) {
    204. return STOP;
    205. }
    206. if (attempt > this.intervals.size()) {
    207. return STOP;
    208. }
    209. return this.intervals.get(attempt - 1);
    210. }
    211. @Override
    212. public long getElapsedTime(int attempt) {
    213. if (attempt >= maxAttempt) {
    214. attempt = maxAttempt;
    215. }
    216. if (attempt >= this.intervals.size()) {
    217. attempt = this.intervals.size();
    218. }
    219. long elapsedTime = 0;
    220. for (int i = 0; i < attempt; i++) {
    221. elapsedTime += this.intervals.get(i);
    222. }
    223. return elapsedTime;
    224. }
    225. public String getSequence() {
    226. return sequence;
    227. }
    228. public void setSequence(String sequence) {
    229. this.sequence = sequence;
    230. }
    231. @Override
    232. public int getMaxAttempt() {
    233. return maxAttempt;
    234. }
    235. @Override
    236. public void setMaxAttempt(int maxAttempt) {
    237. this.maxAttempt = maxAttempt;
    238. }
    239. public String getSeparator() {
    240. return separator;
    241. }
    242. public void setSeparator(String separator) {
    243. this.separator = separator;
    244. }
    245. public List getIntervals() {
    246. return intervals;
    247. }
    248. public void setIntervals(List intervals) {
    249. this.intervals = intervals;
    250. }
    251. public long getCurrentElapsedTime() {
    252. return currentElapsedTime;
    253. }
    254. public void setCurrentElapsedTime(long currentElapsedTime) {
    255. this.currentElapsedTime = currentElapsedTime;
    256. }
    257. public int getCurrentAttempt() {
    258. return currentAttempt;
    259. }
    260. public void setCurrentAttempt(int currentAttempt) {
    261. this.currentAttempt = currentAttempt;
    262. }
    263. @Override
    264. public String toString() {
    265. final StringBuilder sb = new StringBuilder("CustomBackoff{");
    266. sb.append("intervals=").append(intervals);
    267. String attemptValue = (maxAttempt == Integer.MAX_VALUE ?
    268. "unlimited" : String.valueOf(maxAttempt));
    269. sb.append(", maxAttempt=").append(attemptValue);
    270. sb.append(", currentElapsedTime=").append(currentElapsedTime);
    271. sb.append(", currentAttempt=").append(this.currentAttempt);
    272. sb.append('}');
    273. return sb.toString();
    274. }
    275. }
    276. public static void main(String[] args) {
    277. BackoffExecution backoffExecution = new CustomBackoff().start();
    278. System.out.println(backoffExecution.getBackoff(1));
    279. System.out.println(backoffExecution.getBackoff(2));
    280. System.out.println(backoffExecution.getBackoff(3));
    281. System.out.println(backoffExecution.getElapsedTime(1));
    282. System.out.println(backoffExecution.getElapsedTime(2));
    283. System.out.println(backoffExecution.getElapsedTime(3));
    284. }
    285. }

    1. package com.xxx.commons.retry.backoff;
    2. import java.io.Serializable;
    3. import java.util.concurrent.TimeUnit;
    4. /**
    5. * @created 2021-12-20 4:31 PM
    6. * @description:
    7. */
    8. public class ExponentialBackoff implements Backoff, Serializable {
    9. private static final long serialVersionUID = -3955367506662250971L;
    10. /**
    11. * The default initial interval.
    12. */
    13. public static final long DEFAULT_INITIAL_INTERVAL = 2000L;
    14. /**
    15. * The default multiplier (increases the interval by 50%).
    16. */
    17. public static final double DEFAULT_MULTIPLIER = 2;
    18. /**
    19. * The default maximum back off time.
    20. */
    21. public static final long DEFAULT_MAX_INTERVAL = 10 * 60 * 1000L;
    22. /**
    23. * Constant value indicating an unlimited number of attempt.
    24. */
    25. public static final int UNLIMITED_ATTEMPTS = Integer.MAX_VALUE;
    26. private long initialInterval = DEFAULT_INITIAL_INTERVAL;
    27. private double multiplier = DEFAULT_MULTIPLIER;
    28. private long maxInterval = DEFAULT_MAX_INTERVAL;
    29. private int maxAttempt = UNLIMITED_ATTEMPTS;
    30. /**
    31. * Create an instance with the default settings.
    32. *
    33. * @see #DEFAULT_INITIAL_INTERVAL
    34. * @see #DEFAULT_MULTIPLIER
    35. * @see #DEFAULT_MAX_INTERVAL
    36. */
    37. public ExponentialBackoff() {
    38. }
    39. /**
    40. * Create an instance.
    41. *
    42. * @param maxAttempt the maximum number of attempt
    43. */
    44. public ExponentialBackoff(int maxAttempt) {
    45. this.maxAttempt = maxAttempt;
    46. }
    47. /**
    48. * Create an instance with the supplied settings.
    49. *
    50. * @param initialInterval the initial interval in milliseconds
    51. */
    52. public ExponentialBackoff(long initialInterval) {
    53. this.initialInterval = initialInterval;
    54. this.checkInterval(this.initialInterval, this.maxInterval);
    55. }
    56. /**
    57. * Create an instance with the supplied settings.
    58. *
    59. * @param initialInterval the initial interval
    60. * @param timeUnit the initial interval's time unit
    61. * @param multiplier the multiplier (should be greater than or equal to 1)
    62. */
    63. public ExponentialBackoff(long initialInterval, TimeUnit timeUnit, double multiplier) {
    64. checkMultiplier(multiplier);
    65. this.multiplier = multiplier;
    66. this.initialInterval = BackoffExecution.DEFAULT_TIME_UNIT.convert(initialInterval, timeUnit);
    67. this.checkInterval(this.initialInterval, this.maxInterval);
    68. }
    69. public ExponentialBackoff(long initialInterval, long maxInterval, TimeUnit timeUnit, double multiplier) {
    70. checkMultiplier(multiplier);
    71. this.multiplier = multiplier;
    72. this.initialInterval = BackoffExecution.DEFAULT_TIME_UNIT.convert(initialInterval, timeUnit);
    73. this.maxInterval = BackoffExecution.DEFAULT_TIME_UNIT.convert(maxInterval, timeUnit);
    74. this.checkInterval(this.initialInterval, this.maxInterval);
    75. }
    76. /**
    77. * The initial interval in milliseconds.
    78. */
    79. public void setInitialInterval(long initialInterval) {
    80. this.initialInterval = initialInterval;
    81. this.checkInterval(this.initialInterval, this.maxInterval);
    82. }
    83. /**
    84. * Return the initial interval in milliseconds.
    85. */
    86. public long getInitialInterval() {
    87. return initialInterval;
    88. }
    89. /**
    90. * The value to multiply the current interval by for each retry attempt.
    91. */
    92. public void setMultiplier(double multiplier) {
    93. checkMultiplier(multiplier);
    94. this.multiplier = multiplier;
    95. }
    96. /**
    97. * Return the value to multiply the current interval by for each retry attempt.
    98. */
    99. public double getMultiplier() {
    100. return multiplier;
    101. }
    102. /**
    103. * The maximum back off time.
    104. */
    105. public void setMaxInterval(long maxInterval) {
    106. this.maxInterval = maxInterval;
    107. }
    108. /**
    109. * Return the maximum back off time.
    110. */
    111. public long getMaxInterval() {
    112. return maxInterval;
    113. }
    114. /**
    115. * Set the maximum number of attempt in milliseconds.
    116. */
    117. public void setMaxAttempt(int maxAttempt) {
    118. this.maxAttempt = maxAttempt;
    119. }
    120. /**
    121. * Return the maximum number of attempt in milliseconds.
    122. */
    123. public int getMaxAttempt() {
    124. return maxAttempt;
    125. }
    126. @Override
    127. public BackoffExecution start() {
    128. return new ExponentialBackoffExecution(initialInterval, maxInterval, multiplier, maxAttempt);
    129. }
    130. private void checkMultiplier(double multiplier) {
    131. if (multiplier < 1) {
    132. throw new IllegalArgumentException("Invalid multiplier '" + multiplier + "'. Should be equal" +
    133. "or higher than 1. A multiplier of 1 is equivalent to a fixed interval");
    134. }
    135. }
    136. private void checkInterval(long initialInterval, long maxInterval) {
    137. if (initialInterval <= 0) {
    138. throw new IllegalArgumentException("Invalid initialInterval '" + initialInterval + "'. Should be higher" +
    139. " than 0. A initialInterval of 0 is equivalent to no interval");
    140. }
    141. if (maxInterval <= initialInterval * getMultiplier()) {
    142. throw new IllegalArgumentException("Invalid maxInterval '" + maxInterval + "'. Should be higher" +
    143. " than initialInterval * multiplier. A maxInterval equal or less than initialInterval * multiplier " +
    144. "is equivalent to a fixed interval");
    145. }
    146. }
    147. public static class ExponentialBackoffExecution implements BackoffExecution, Serializable {
    148. private static final long serialVersionUID = 672748521748221970L;
    149. private long initialInterval;
    150. private double multiplier;
    151. private long maxInterval;
    152. private int maxAttempt;
    153. private long currentInterval = -1;
    154. private long currentElapsedTime = 0;
    155. private int currentAttempt = 0;
    156. public ExponentialBackoffExecution() {
    157. this(DEFAULT_INITIAL_INTERVAL, DEFAULT_MAX_INTERVAL, DEFAULT_MULTIPLIER, UNLIMITED_ATTEMPTS);
    158. }
    159. public ExponentialBackoffExecution(long initialInterval, long maxInterval, double multiplier, int maxAttempt) {
    160. this.initialInterval = initialInterval;
    161. this.maxInterval = maxInterval;
    162. this.multiplier = multiplier;
    163. this.maxAttempt = maxAttempt;
    164. }
    165. @Override
    166. public int currentAttempt() {
    167. return currentAttempt;
    168. }
    169. @Override
    170. public long getElapsedTime() {
    171. return this.currentElapsedTime;
    172. }
    173. @Override
    174. public long nextBackoff() {
    175. if (currentAttempt >= maxAttempt) {
    176. return STOP;
    177. }
    178. long nextInterval = computeNextInterval();
    179. this.currentElapsedTime += nextInterval;
    180. this.currentAttempt += 1;
    181. return nextInterval;
    182. }
    183. private long computeNextInterval() {
    184. long maxInterval = this.maxInterval;
    185. if (this.currentInterval >= maxInterval) {
    186. return maxInterval;
    187. } else if (this.currentInterval < 0) {
    188. this.currentInterval = this.initialInterval;
    189. } else {
    190. this.currentInterval = multiplyInterval(this.currentInterval, maxInterval);
    191. }
    192. return this.currentInterval;
    193. }
    194. private long multiplyInterval(long currentInterval, long maxInterval) {
    195. long interval = currentInterval;
    196. interval *= this.multiplier;
    197. return (interval > maxInterval ? maxInterval : interval);
    198. }
    199. @Override
    200. public void updateCurrentAttempt(int attempt) {
    201. if (attempt < 0) {
    202. this.currentAttempt = 0;
    203. } else if (attempt >= maxAttempt) {
    204. this.currentAttempt = maxAttempt;
    205. } else {
    206. this.currentAttempt = attempt;
    207. }
    208. this.currentElapsedTime = this.getElapsedTime(this.currentAttempt);
    209. }
    210. @Override
    211. public long getBackoff(int attempt) {
    212. if (attempt <= 0) {
    213. return 0L;
    214. }
    215. if (attempt - 1 >= maxAttempt) {
    216. return STOP;
    217. }
    218. long interval = this.initialInterval;
    219. long maxInterval = this.maxInterval;
    220. for (long i = 1; i < attempt; i++) {
    221. interval = multiplyInterval(interval, maxInterval);
    222. if (interval >= maxInterval) {
    223. break;
    224. }
    225. }
    226. return interval;
    227. }
    228. @Override
    229. public long getElapsedTime(int attempt) {
    230. if (attempt >= maxAttempt) {
    231. attempt = maxAttempt;
    232. }
    233. long interval = this.initialInterval;
    234. long maxInterval = this.maxInterval;
    235. long elapsedTime = 0;
    236. if (attempt <= 0) {
    237. return elapsedTime;
    238. } else {
    239. elapsedTime = interval;
    240. }
    241. for (long i = 1; i < attempt; i++) {
    242. if (interval < maxInterval) {
    243. interval = multiplyInterval(interval, maxInterval);
    244. }
    245. elapsedTime += interval;
    246. }
    247. return elapsedTime;
    248. }
    249. public long getInitialInterval() {
    250. return initialInterval;
    251. }
    252. public void setInitialInterval(long initialInterval) {
    253. this.initialInterval = initialInterval;
    254. }
    255. public double getMultiplier() {
    256. return multiplier;
    257. }
    258. public void setMultiplier(double multiplier) {
    259. this.multiplier = multiplier;
    260. }
    261. public long getMaxInterval() {
    262. return maxInterval;
    263. }
    264. public void setMaxInterval(long maxInterval) {
    265. this.maxInterval = maxInterval;
    266. }
    267. @Override
    268. public int getMaxAttempt() {
    269. return maxAttempt;
    270. }
    271. @Override
    272. public void setMaxAttempt(int maxAttempt) {
    273. this.maxAttempt = maxAttempt;
    274. }
    275. public long getCurrentInterval() {
    276. return currentInterval;
    277. }
    278. public void setCurrentInterval(long currentInterval) {
    279. this.currentInterval = currentInterval;
    280. }
    281. public long getCurrentElapsedTime() {
    282. return currentElapsedTime;
    283. }
    284. public void setCurrentElapsedTime(long currentElapsedTime) {
    285. this.currentElapsedTime = currentElapsedTime;
    286. }
    287. public int getCurrentAttempt() {
    288. return currentAttempt;
    289. }
    290. public void setCurrentAttempt(int currentAttempt) {
    291. this.currentAttempt = currentAttempt;
    292. }
    293. @Override
    294. public String toString() {
    295. StringBuilder sb = new StringBuilder("ExponentialBackOff{");
    296. sb.append("currentInterval=").append(this.currentInterval < 0 ? "n/a" : this.currentInterval + "ms");
    297. sb.append(", multiplier=").append(this.multiplier);
    298. sb.append('}');
    299. return sb.toString();
    300. }
    301. }
    302. }
    1. package com.xxx.commons.retry.backoff;
    2. import java.io.Serializable;
    3. import java.util.concurrent.TimeUnit;
    4. /**
    5. * @created 2021-12-20 4:32 PM
    6. * @description:
    7. */
    8. public class FixedBackoff implements Backoff, Serializable {
    9. private static final long serialVersionUID = 2511140811757045231L;
    10. /**
    11. * The default recovery interval: 5000 ms = 5 seconds.
    12. */
    13. public static final long DEFAULT_INTERVAL = 5000L;
    14. /**
    15. * Constant value indicating an unlimited number of attempt.
    16. */
    17. public static final int UNLIMITED_ATTEMPTS = Integer.MAX_VALUE;
    18. private long interval = DEFAULT_INTERVAL;
    19. private int maxAttempt = UNLIMITED_ATTEMPTS;
    20. /**
    21. * Create an instance with an interval of {@value #DEFAULT_INTERVAL}
    22. * ms and an unlimited number of attempt.
    23. */
    24. public FixedBackoff() {
    25. }
    26. /**
    27. * Create an instance.
    28. *
    29. * @param maxAttempt the maximum number of attempt
    30. */
    31. public FixedBackoff(int maxAttempt) {
    32. this.maxAttempt = maxAttempt;
    33. }
    34. /**
    35. * Create an instance.
    36. *
    37. * @param interval the interval between two attempt
    38. * @param timeUnit the time unit of interval
    39. */
    40. public FixedBackoff(long interval, TimeUnit timeUnit) {
    41. this.interval = BackoffExecution.DEFAULT_TIME_UNIT.convert(interval, timeUnit);
    42. this.maxAttempt = maxAttempt;
    43. }
    44. /**
    45. * Create an instance.
    46. *
    47. * @param maxAttempt the maximum number of attempt
    48. * @param interval the interval between two attempt
    49. */
    50. public FixedBackoff(int maxAttempt, long interval) {
    51. this.interval = interval;
    52. this.maxAttempt = maxAttempt;
    53. }
    54. /**
    55. * Create an instance.
    56. *
    57. * @param maxAttempt the maximum number of attempt
    58. * @param interval the interval between two attempt
    59. * @param timeUnit the time unit of interval
    60. */
    61. public FixedBackoff(int maxAttempt, long interval, TimeUnit timeUnit) {
    62. this.interval = BackoffExecution.DEFAULT_TIME_UNIT.convert(interval, timeUnit);
    63. this.maxAttempt = maxAttempt;
    64. }
    65. /**
    66. * Set the interval between two attempt in milliseconds.
    67. */
    68. public void setInterval(long interval) {
    69. this.interval = interval;
    70. }
    71. /**
    72. * Return the interval between two attempt in milliseconds.
    73. */
    74. public long getInterval() {
    75. return interval;
    76. }
    77. /**
    78. * Set the maximum number of attempt in milliseconds.
    79. */
    80. public void setMaxAttempt(int maxAttempt) {
    81. this.maxAttempt = maxAttempt;
    82. }
    83. /**
    84. * Return the maximum number of attempt in milliseconds.
    85. */
    86. public int getMaxAttempt() {
    87. return maxAttempt;
    88. }
    89. @Override
    90. public BackoffExecution start() {
    91. return new FixedBackoffExecution(interval, maxAttempt);
    92. }
    93. public static class FixedBackoffExecution implements BackoffExecution, Serializable {
    94. private static final long serialVersionUID = 1656175761226107038L;
    95. private long interval;
    96. private int maxAttempt;
    97. private int currentAttempt = 0;
    98. private long currentElapsedTime = 0;
    99. public FixedBackoffExecution() {
    100. this(DEFAULT_INTERVAL, UNLIMITED_ATTEMPTS);
    101. }
    102. public FixedBackoffExecution(long interval, int maxAttempt) {
    103. this.interval = interval;
    104. this.maxAttempt = maxAttempt;
    105. }
    106. public long getInterval() {
    107. return interval;
    108. }
    109. public void setInterval(long interval) {
    110. this.interval = interval;
    111. }
    112. @Override
    113. public int getMaxAttempt() {
    114. return maxAttempt;
    115. }
    116. @Override
    117. public void setMaxAttempt(int maxAttempt) {
    118. this.maxAttempt = maxAttempt;
    119. }
    120. public int getCurrentAttempt() {
    121. return currentAttempt;
    122. }
    123. public void setCurrentAttempt(int currentAttempt) {
    124. this.currentAttempt = currentAttempt;
    125. }
    126. public long getCurrentElapsedTime() {
    127. return currentElapsedTime;
    128. }
    129. public void setCurrentElapsedTime(long currentElapsedTime) {
    130. this.currentElapsedTime = currentElapsedTime;
    131. }
    132. @Override
    133. public int currentAttempt() {
    134. return currentAttempt;
    135. }
    136. @Override
    137. public long getElapsedTime() {
    138. return this.currentElapsedTime;
    139. }
    140. @Override
    141. public long nextBackoff() {
    142. if (this.currentAttempt < this.maxAttempt) {
    143. long nextInterval = this.interval;
    144. this.currentElapsedTime += nextInterval;
    145. this.currentAttempt++;
    146. return nextInterval;
    147. } else {
    148. return STOP;
    149. }
    150. }
    151. @Override
    152. public void updateCurrentAttempt(int attempt) {
    153. if (attempt < 0) {
    154. this.currentAttempt = 0;
    155. } else if (attempt >= maxAttempt) {
    156. this.currentAttempt = maxAttempt;
    157. } else {
    158. this.currentAttempt = attempt;
    159. }
    160. this.currentElapsedTime = this.getElapsedTime(this.currentAttempt);
    161. }
    162. @Override
    163. public long getBackoff(int attempt) {
    164. if (attempt <= 0) {
    165. return 0L;
    166. }
    167. if (attempt - 1 >= maxAttempt) {
    168. return STOP;
    169. }
    170. return this.interval;
    171. }
    172. @Override
    173. public long getElapsedTime(int attempt) {
    174. return this.interval * attempt;
    175. }
    176. @Override
    177. public String toString() {
    178. final StringBuilder sb = new StringBuilder("FixedBackoff{");
    179. sb.append("interval=").append(this.interval);
    180. String attemptValue = (this.maxAttempt == Integer.MAX_VALUE ?
    181. "unlimited" : String.valueOf(this.maxAttempt));
    182. sb.append(", maxAttempt=").append(attemptValue);
    183. sb.append(", currentAttempt=").append(this.currentAttempt);
    184. sb.append('}');
    185. return sb.toString();
    186. }
    187. }
    188. }
    1. package com.xxx.commons.retry.backoff;
    2. import java.io.Serializable;
    3. import java.util.concurrent.TimeUnit;
    4. import static com.xxx.commons.retry.backoff.BackoffExecution.DEFAULT_TIME_UNIT;
    5. /**
    6. * @created 2021-12-20 4:33 PM
    7. * @description:
    8. */
    9. public class GradualBackoff implements Backoff, Serializable {
    10. private static final long serialVersionUID = -4082336395861849848L;
    11. /**
    12. * The default initial interval.
    13. */
    14. public static final long DEFAULT_INITIAL_INTERVAL = 2000L;
    15. /**
    16. * The default recovery interval: 3000 ms = 3 seconds.
    17. */
    18. public static final long DEFAULT_INTERVAL = 3000L;
    19. /**
    20. * Constant value indicating an unlimited number of attempt.
    21. */
    22. public static final int UNLIMITED_ATTEMPTS = Integer.MAX_VALUE;
    23. private long initialInterval = DEFAULT_INITIAL_INTERVAL;
    24. private long interval = DEFAULT_INTERVAL;
    25. private int maxAttempt = UNLIMITED_ATTEMPTS;
    26. /**
    27. * Create an instance with an interval of {@value #DEFAULT_INTERVAL}
    28. * ms and an unlimited number of attempt.
    29. */
    30. public GradualBackoff() {
    31. }
    32. /**
    33. * Create an instance.
    34. *
    35. * @param maxAttempt the maximum number of attempt
    36. */
    37. public GradualBackoff(int maxAttempt) {
    38. this.maxAttempt = maxAttempt;
    39. }
    40. /**
    41. * Create an instance.
    42. *
    43. * @param interval the interval between two attempt
    44. * @param timeUnit the time unit of interval
    45. */
    46. public GradualBackoff(long initialInterval, long interval, TimeUnit timeUnit) {
    47. this.initialInterval = DEFAULT_TIME_UNIT.convert(initialInterval, timeUnit);
    48. this.interval = DEFAULT_TIME_UNIT.convert(interval, timeUnit);
    49. this.maxAttempt = maxAttempt;
    50. }
    51. /**
    52. * Create an instance.
    53. *
    54. * @param maxAttempt the maximum number of attempt
    55. * @param interval the interval between two attempt
    56. */
    57. public GradualBackoff(int maxAttempt, long initialInterval, long interval) {
    58. this.initialInterval = initialInterval;
    59. this.interval = interval;
    60. this.maxAttempt = maxAttempt;
    61. }
    62. /**
    63. * Create an instance.
    64. *
    65. * @param maxAttempt the maximum number of attempt
    66. * @param interval the interval between two attempt
    67. * @param timeUnit the time unit of interval
    68. */
    69. public GradualBackoff(int maxAttempt, long initialInterval, long interval, TimeUnit timeUnit) {
    70. this.initialInterval = DEFAULT_TIME_UNIT.convert(initialInterval, timeUnit);
    71. this.interval = DEFAULT_TIME_UNIT.convert(interval, timeUnit);
    72. this.maxAttempt = maxAttempt;
    73. }
    74. /**
    75. * Return the interval of first attempt in milliseconds.
    76. *
    77. * @return
    78. */
    79. public long getInitialInterval() {
    80. return initialInterval;
    81. }
    82. /**
    83. * Set the interval of first attempt in milliseconds.
    84. */
    85. public void setInitialInterval(long initialInterval) {
    86. this.initialInterval = initialInterval;
    87. }
    88. /**
    89. * Set the interval between two attempt in milliseconds.
    90. */
    91. public void setInterval(long interval) {
    92. this.interval = interval;
    93. }
    94. /**
    95. * Return the interval between two attempt in milliseconds.
    96. */
    97. public long getInterval() {
    98. return interval;
    99. }
    100. /**
    101. * Set the maximum number of attempt in milliseconds.
    102. */
    103. public void setMaxAttempt(int maxAttempt) {
    104. this.maxAttempt = maxAttempt;
    105. }
    106. /**
    107. * Return the maximum number of attempt in milliseconds.
    108. */
    109. public int getMaxAttempt() {
    110. return maxAttempt;
    111. }
    112. @Override
    113. public BackoffExecution start() {
    114. return new GradualBackoffExecution(initialInterval, interval, maxAttempt);
    115. }
    116. public static class GradualBackoffExecution implements BackoffExecution, Serializable {
    117. private static final long serialVersionUID = 6691539566098442985L;
    118. private long initialInterval;
    119. private long interval;
    120. private int maxAttempt;
    121. private int currentAttempt = 0;
    122. private long currentElapsedTime = 0;
    123. public GradualBackoffExecution() {
    124. this(DEFAULT_INITIAL_INTERVAL, DEFAULT_INTERVAL, UNLIMITED_ATTEMPTS);
    125. }
    126. public GradualBackoffExecution(long initialInterval, long interval, int maxAttempt) {
    127. this.initialInterval = initialInterval;
    128. this.interval = interval;
    129. this.maxAttempt = maxAttempt;
    130. }
    131. @Override
    132. public int currentAttempt() {
    133. return currentAttempt;
    134. }
    135. @Override
    136. public long getElapsedTime() {
    137. return this.currentElapsedTime;
    138. }
    139. @Override
    140. public long nextBackoff() {
    141. if (this.currentAttempt < this.maxAttempt) {
    142. long nextInterval = this.currentAttempt == 0 ? this.initialInterval : this.interval;
    143. this.currentElapsedTime += nextInterval;
    144. this.currentAttempt++;
    145. return nextInterval;
    146. } else {
    147. return STOP;
    148. }
    149. }
    150. @Override
    151. public void updateCurrentAttempt(int attempt) {
    152. if (attempt < 0) {
    153. this.currentAttempt = 0;
    154. } else if (attempt >= maxAttempt) {
    155. this.currentAttempt = maxAttempt;
    156. } else {
    157. this.currentAttempt = attempt;
    158. }
    159. this.currentElapsedTime = this.getElapsedTime(this.currentAttempt);
    160. }
    161. @Override
    162. public long getBackoff(int attempt) {
    163. if (attempt <= 0) {
    164. return 0L;
    165. }
    166. if (attempt - 1 >= maxAttempt) {
    167. return STOP;
    168. }
    169. return attempt == 1 ? this.initialInterval : this.interval;
    170. }
    171. @Override
    172. public long getElapsedTime(int attempt) {
    173. return attempt == 1 ? this.initialInterval : this.initialInterval + this.interval * (attempt - 1);
    174. }
    175. public long getInitialInterval() {
    176. return initialInterval;
    177. }
    178. public void setInitialInterval(long initialInterval) {
    179. this.initialInterval = initialInterval;
    180. }
    181. public long getInterval() {
    182. return interval;
    183. }
    184. public void setInterval(long interval) {
    185. this.interval = interval;
    186. }
    187. @Override
    188. public int getMaxAttempt() {
    189. return maxAttempt;
    190. }
    191. @Override
    192. public void setMaxAttempt(int maxAttempt) {
    193. this.maxAttempt = maxAttempt;
    194. }
    195. public int getCurrentAttempt() {
    196. return currentAttempt;
    197. }
    198. public void setCurrentAttempt(int currentAttempt) {
    199. this.currentAttempt = currentAttempt;
    200. }
    201. public long getCurrentElapsedTime() {
    202. return currentElapsedTime;
    203. }
    204. public void setCurrentElapsedTime(long currentElapsedTime) {
    205. this.currentElapsedTime = currentElapsedTime;
    206. }
    207. @Override
    208. public String toString() {
    209. final StringBuilder sb = new StringBuilder("GradualBackoff{");
    210. sb.append("initialInterval=").append(this.initialInterval);
    211. sb.append("interval=").append(this.interval);
    212. String attemptValue = (this.maxAttempt == Integer.MAX_VALUE ?
    213. "unlimited" : String.valueOf(this.maxAttempt));
    214. sb.append(", maxAttempt=").append(attemptValue);
    215. sb.append(", currentAttempt=").append(this.currentAttempt);
    216. sb.append('}');
    217. return sb.toString();
    218. }
    219. }
    220. }
    1. package com.xxx.commons.retry.backoff;
    2. import java.io.Serializable;
    3. /**
    4. * @created 2021-12-20 4:34 PM
    5. * @description:
    6. */
    7. public class NoneBackoff implements Backoff, Serializable {
    8. private static final long serialVersionUID = 8227959838869099892L;
    9. /**
    10. * The default recovery interval: 0 ms = 0 seconds.
    11. */
    12. public static final long DEFAULT_INTERVAL = 0L;
    13. /**
    14. * Constant value indicating an unlimited number of attempt.
    15. */
    16. public static final int UNLIMITED_ATTEMPTS = Integer.MAX_VALUE;
    17. private long interval = DEFAULT_INTERVAL;
    18. private int maxAttempt = UNLIMITED_ATTEMPTS;
    19. /**
    20. * Create an instance with an interval of {@value #DEFAULT_INTERVAL}
    21. * ms and an unlimited number of attempt.
    22. */
    23. public NoneBackoff() {
    24. }
    25. /**
    26. * Create an instance.
    27. *
    28. * @param maxAttempt the maximum number of attempt
    29. */
    30. public NoneBackoff(int maxAttempt) {
    31. this.maxAttempt = maxAttempt;
    32. }
    33. /**
    34. * Return the interval between two attempt in milliseconds.
    35. */
    36. public long getInterval() {
    37. return interval;
    38. }
    39. /**
    40. * Return the maximum number of attempt in milliseconds.
    41. */
    42. public int getMaxAttempt() {
    43. return maxAttempt;
    44. }
    45. /**
    46. * Set the maximum number of attempt in milliseconds.
    47. */
    48. public void setMaxAttempt(int maxAttempt) {
    49. this.maxAttempt = maxAttempt;
    50. }
    51. @Override
    52. public BackoffExecution start() {
    53. return new NoneBackoffExecution(interval, maxAttempt);
    54. }
    55. public static class NoneBackoffExecution implements BackoffExecution, Serializable {
    56. private static final long serialVersionUID = 2453613900451888865L;
    57. private long interval;
    58. private int maxAttempt;
    59. private int currentAttempt = 0;
    60. private long currentElapsedTime = 0;
    61. public NoneBackoffExecution() {
    62. this(DEFAULT_INTERVAL, UNLIMITED_ATTEMPTS);
    63. }
    64. public NoneBackoffExecution(long interval, int maxAttempt) {
    65. this.interval = interval;
    66. this.maxAttempt = maxAttempt;
    67. }
    68. @Override
    69. public int currentAttempt() {
    70. return currentAttempt;
    71. }
    72. @Override
    73. public long getElapsedTime() {
    74. return this.currentElapsedTime;
    75. }
    76. @Override
    77. public long nextBackoff() {
    78. if (this.currentAttempt < this.maxAttempt) {
    79. long nextInterval = this.interval;
    80. this.currentElapsedTime += nextInterval;
    81. this.currentAttempt++;
    82. return nextInterval;
    83. } else {
    84. return STOP;
    85. }
    86. }
    87. @Override
    88. public void updateCurrentAttempt(int attempt) {
    89. if (attempt < 0) {
    90. this.currentAttempt = 0;
    91. } else if (attempt >= maxAttempt) {
    92. this.currentAttempt = maxAttempt;
    93. } else {
    94. this.currentAttempt = attempt;
    95. }
    96. this.currentElapsedTime = this.getElapsedTime(this.currentAttempt);
    97. }
    98. @Override
    99. public long getBackoff(int attempt) {
    100. if (attempt <= 0) {
    101. return 0L;
    102. }
    103. if (attempt - 1 >= maxAttempt) {
    104. return STOP;
    105. }
    106. return this.interval;
    107. }
    108. @Override
    109. public long getElapsedTime(int attempt) {
    110. return this.interval * attempt;
    111. }
    112. public long getInterval() {
    113. return interval;
    114. }
    115. public void setInterval(long interval) {
    116. this.interval = interval;
    117. }
    118. @Override
    119. public int getMaxAttempt() {
    120. return maxAttempt;
    121. }
    122. @Override
    123. public void setMaxAttempt(int maxAttempt) {
    124. this.maxAttempt = maxAttempt;
    125. }
    126. public int getCurrentAttempt() {
    127. return currentAttempt;
    128. }
    129. public void setCurrentAttempt(int currentAttempt) {
    130. this.currentAttempt = currentAttempt;
    131. }
    132. public long getCurrentElapsedTime() {
    133. return currentElapsedTime;
    134. }
    135. public void setCurrentElapsedTime(long currentElapsedTime) {
    136. this.currentElapsedTime = currentElapsedTime;
    137. }
    138. @Override
    139. public String toString() {
    140. final StringBuilder sb = new StringBuilder("NoneBackoff{");
    141. sb.append("interval=").append(this.interval);
    142. sb.append(", maxAttempt=").append(this.maxAttempt);
    143. sb.append('}');
    144. return sb.toString();
    145. }
    146. }
    147. }

    2.2.3 业务使用示例

    网关使用示例:

    • 添加依赖

    1. <!-- 公共模块 -->
    2. <dependency>
    3. <groupId>com.xxx.arch</groupId>
    4. <artifactId>xxx-commons-recipes</artifactId>
    5. <version>1.0.8</version>
    6. <!-- <version>${xxx.commons.version}</version> -->
    7. </dependency>
    • 初始化

    1. @Configuration
    2. @AutoConfigureAfter(OnsConfig.class)
    3. public class LinkBean {
    4. 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";
    5. @Bean
    6. public BackoffExecution backoffExecution() {
    7. return new CustomBackoff(MESSAGE_DELAY_LEVEL, MAX_RETRY_COUNT).start();
    8. }
    9. ...
    10. }
    • 具体使用

    1. @Slf4j
    2. public class RetryFilterFactory implements FilterFactory {
    3. private Producer producer;
    4. private BackoffExecution backoffExecution;
    5. public RetryFilterFactory(Producer producer, BackoffExecution backoffExecution) {
    6. this.producer = producer;
    7. this.backoffExecution = backoffExecution;
    8. }
    9. @Override
    10. public String name() {
    11. return "retry";
    12. }
    13. @Override
    14. public Filter apply(Properties properties) {
    15. return (context, chain) -> {
    16. int retry = context.getRetryCount();
    17. retry++;
    18. long retryTime = context.getAttributeOrDefault(LinkContext.FAILOVER_RETRY_TIME_ATTRIBUTE, 0L);
    19. context.getAttributes().put(LinkContext.FAILOVER_RETRY_TIME_ATTRIBUTE,
    20. retryTime + backoffExecution.getBackoff(retry));
    21. try {
    22. producer.send(context);
    23. } catch (Exception exp) {
    24. log.error("master send context: {}, error:", context, exp);
    25. throw new LinkIOException("send master mq error");
    26. }
    27. chain.filter(context);
    28. };
    29. }
    30. }

    四、未来展望

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

  • 相关阅读:
    Java寻找两个正序数组的中位数
    推挽输出和开漏输出-三极管-mos管
    [附源码]计算机毕业设计新冠疫苗接种预约系统Springboot程序
    利用编码特长,我赚取了每月1000美元的额外收入
    ByteBuffer杂记
    c/c++里 对 共用体 union 的内存分配
    matlab如何实现任意长序列所有排列方式
    吃鸡专家教你战无不胜,分享顶级干货!
    C专家编程 第1章 C:穿越时空的迷雾 1.4 K&R C
    PMP考试如何退考?手把手教你
  • 原文地址:https://blog.csdn.net/shallowgrave/article/details/133937678