├── .gitignore ├── .idea ├── .gitignore ├── ApifoxUploaderProjectSetting.xml ├── aws.xml ├── encodings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── uiDesigner.xml └── vcs.xml ├── README-zh.md ├── README.md ├── pom.xml ├── retry-annotation ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── poldroc │ └── retry │ └── annotation │ ├── annotation │ ├── Retry.java │ ├── RetryWait.java │ └── metadata │ │ ├── RetryAble.java │ │ └── RetryWaitAble.java │ ├── core │ └── RetryTemplate.java │ ├── handler │ ├── RetryAbleHandler.java │ ├── RetryWaitAbleHandler.java │ ├── impl │ │ ├── DefaultRetryAbleHandler.java │ │ └── DefaultRetryWaitAbleHandler.java │ └── method │ │ └── RetryMethodHandler.java │ ├── model │ └── RetryAbleBean.java │ └── proxy │ ├── IMethodHandler.java │ ├── IProxy.java │ ├── cglib │ └── CglibProxy.java │ ├── dynamic │ └── DynamicProxy.java │ └── none │ └── NoneProxy.java ├── retry-api ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── poldroc │ └── retry │ └── api │ ├── context │ ├── RetryContext.java │ └── RetryWaitContext.java │ ├── core │ └── Retry.java │ ├── exception │ └── RetryException.java │ ├── model │ ├── AttemptTime.java │ ├── RetryAttempt.java │ └── WaitTime.java │ └── support │ ├── block │ └── RetryBlock.java │ ├── condition │ └── RetryCondition.java │ ├── listen │ └── RetryListen.java │ ├── recover │ └── Recover.java │ ├── stop │ └── RetryStop.java │ └── wait │ └── RetryWait.java ├── retry-common ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── poldroc │ └── retry │ └── common │ ├── annotation │ ├── NotThreadSafe.java │ └── ThreadSafe.java │ ├── constant │ └── enums │ │ └── ProxyTypeEnum.java │ ├── support │ ├── instance │ │ ├── Instance.java │ │ └── impl │ │ │ └── InstanceFactory.java │ └── proxy │ │ └── ProxyFactory.java │ └── util │ ├── ArgUtil.java │ └── StringUtil.java ├── retry-core ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── poldroc │ └── retry │ └── core │ ├── constant │ └── RetryWaitConst.java │ ├── context │ ├── DefaultRetryContext.java │ └── DefaultRetryWaitContext.java │ ├── core │ ├── RetryWaiter.java │ ├── Retryer.java │ └── retry │ │ └── DefaultRetry.java │ ├── model │ ├── DefaultAttemptTime.java │ ├── DefaultRetryAttempt.java │ └── DefaultWaitTime.java │ └── support │ ├── block │ └── ThreadSleepRetryBlock.java │ ├── condition │ ├── AbstractCauseRetryCondition.java │ ├── AbstractResultRetryCondition.java │ ├── AbstractRetryConditionInit.java │ ├── AbstractTimeRetryCondition.java │ ├── AlwaysFalseRetryCondition.java │ ├── AlwaysTrueRetryCondition.java │ ├── ExceptionCauseRetryCondition.java │ ├── NotNullResultRetryCondition.java │ ├── NullResultRetryCondition.java │ └── RetryConditions.java │ ├── listen │ ├── AbstractRetryListenInit.java │ ├── NoRetryListen.java │ └── RetryListens.java │ ├── recover │ ├── NoRecover.java │ └── Recovers.java │ ├── stop │ └── MaxAttemptRetryStop.java │ └── wait │ ├── AbstractRetryWait.java │ ├── ExponentialRetryWait.java │ ├── FixedRetryWait.java │ ├── IncreaseRetryWait.java │ └── NoRetryWait.java ├── retry-spring ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── poldroc │ └── retry │ └── spring │ ├── annotation │ └── EnableRetry.java │ ├── aop │ └── RetryAop.java │ └── config │ └── RetryAopConfig.java ├── retry-springboot-starter ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── poldroc │ │ └── retry │ │ └── springboot │ │ └── starter │ │ └── config │ │ └── RocRetryAutoConfig.java │ └── resources │ └── META-INF │ └── spring.factories └── retry-test ├── pom.xml └── src ├── main └── java │ └── com │ └── poldroc │ └── retry │ └── test │ ├── RocRetryApplication.java │ ├── service │ ├── SpringRecoverService.java │ ├── SpringService.java │ └── impl │ │ ├── SpringRecoverServiceImpl.java │ │ └── SpringServiceImpl.java │ └── support │ ├── listens │ ├── CustomRetryListen.java │ ├── MyListen1.java │ ├── MyListen2.java │ ├── MyListen3.java │ └── MyListens.java │ └── recover │ └── MyRecover.java └── test └── java └── com └── poldroc └── retry └── test ├── core ├── RetryTemplateTest.java └── RetryerTest.java ├── service ├── UserService.java └── impl │ └── UserServiceImpl.java └── springboot └── RocRetryApplicationTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/ApifoxUploaderProjectSetting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/aws.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 71 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # roc-retry 2 | 3 | roc-retry 是支持过程式编程和注解编程的 java 重试框架 4 | 5 | 6 | 7 | ## **特性描述** 8 | 9 | * 使用 **Builder 模式** ,支持优雅的 **Fluent API** 编程风格 10 | * 基于CGLIB字节码的代理重试 11 | * 基于**注解**的重试机制,允许用户自定义注解 12 | * 允许**依赖异常、返回值的某个状态**来作为触发重试的条件。 13 | * 提供**多种支持策略**,包括阻塞、监听、恢复、等待和终止等策略 14 | * 采用 Netty 类似的接口**API设计**思想,保证接口的一致性,和替换的灵活性 15 | * 无缝接入 Spring\Spring-Boot 16 | 17 | 18 | 19 | ## 快速开始 20 | 21 | ### 引入 22 | 23 | ```xml 24 | 25 | io.github.poldroc 26 | retry-core 27 | 1.1 28 | 29 | ``` 30 | 31 | 32 | 33 | ### 简单入门 34 | 35 | ```java 36 | public class RetryerTest { 37 | /** 38 | * 默认异常进行重试 39 | */ 40 | @Test(expected = RuntimeException.class) 41 | public void helloTest() { 42 | Retryer.newInstance() 43 | .callable(new Callable() { 44 | @Override 45 | public String call() throws Exception { 46 | System.out.println("called..."); 47 | throw new RuntimeException(); 48 | } 49 | }).retryCall(); 50 | } 51 | 52 | /** 53 | * 默认配置测试 54 | */ 55 | @Test(expected = RuntimeException.class) 56 | public void defaultConfigTest() { 57 | Retryer.newInstance() 58 | .maxAttempt(4) 59 | .listen(RetryListens.noListen()) 60 | .recover(Recovers.noRecover()) 61 | .condition(RetryConditions.hasExceptionCause()) 62 | .retryWaitContext(RetryWaiter.retryWait(NoRetryWait.class).context()) 63 | .callable(new Callable() { 64 | @Override 65 | public String call() throws Exception { 66 | System.out.println("called..."); 67 | throw new RuntimeException(); 68 | } 69 | }).retryCall(); 70 | } 71 | } 72 | ``` 73 | 74 | 75 | 76 | 77 | 78 | ## 注解使用 79 | 80 | ### 引入 81 | 82 | ```xml 83 | 84 | io.github.poldroc 85 | retry-annotation 86 | 1.1 87 | 88 | ``` 89 | 90 | [使用详情](https://github.com/Poldroc/roc-retry/blob/master/retry-test/src/test/java/com/poldroc/retry/test/springboot/RocRetryApplicationTest.java) 91 | 92 | ### Retry 93 | 94 | 用于指定重试的相关配置 95 | 96 | ```java 97 | @Documented 98 | @Inherited 99 | @Target(ElementType.METHOD) 100 | @Retention(RetentionPolicy.RUNTIME) 101 | @RetryAble(DefaultRetryAbleHandler.class) 102 | public @interface Retry { 103 | 104 | /** 105 | * 重试类实现 106 | * 107 | * @return 重试 108 | */ 109 | Class retry() default DefaultRetry.class; 110 | 111 | /** 112 | * 最大尝试次数 113 | * 1. 默认为3 包含方法第一次正常执行的次数 114 | * 115 | * @return 次数 116 | */ 117 | int maxAttempt() default 3; 118 | 119 | /** 120 | * 重试触发的场景 121 | * 1. 默认为异常触发 122 | * 123 | * @return 重试触发的场景 124 | */ 125 | Class condition() default ExceptionCauseRetryCondition.class; 126 | 127 | /** 128 | * 监听器 129 | * 1. 默认不进行监听 130 | * 131 | * @return 监听器 132 | */ 133 | Class listen() default NoRetryListen.class; 134 | 135 | /** 136 | * 恢复操作 137 | * 1. 默认不进行任何恢复操作 138 | * 139 | * @return 恢复操作对应的类 140 | */ 141 | Class recover() default NoRecover.class; 142 | 143 | /** 144 | * 重试等待策略 145 | * 1. 支持指定多个,如果不指定,则不进行任何等待, 146 | * 147 | * @return 重试等待策略 148 | */ 149 | RetryWait[] waits() default {}; 150 | } 151 | 152 | ``` 153 | 154 | 155 | 156 | ### RetryWait 157 | 158 | 用于指定重试的等待策略 159 | 160 | ```java 161 | @Retention(RetentionPolicy.RUNTIME) 162 | @Inherited 163 | @Documented 164 | @Target(ElementType.ANNOTATION_TYPE) 165 | @RetryWaitAble(DefaultRetryWaitAbleHandler.class) 166 | public @interface RetryWait { 167 | 168 | /** 169 | * 默认值 170 | * 1. fixed 模式,则对应固定等待时间 171 | * 2. 递增 172 | * @return 默认值 173 | */ 174 | long value() default RetryWaitConst.DEFAULT_VALUE_MILLS; 175 | 176 | /** 177 | * 最小值 178 | * @return 最小值 179 | */ 180 | long min() default RetryWaitConst.DEFAULT_MIN_MILLS; 181 | 182 | /** 183 | * 最大值 184 | * @return 最大值 185 | */ 186 | long max() default RetryWaitConst.DEFAULT_MAX_MILLS; 187 | 188 | /** 189 | * 影响因数 190 | * 1. 递增重试,默认为 {@link RetryWaitConst#INCREASE_MILLS_FACTOR} 191 | * 2. 指数模式。默认为 {@link RetryWaitConst#MULTIPLY_FACTOR} 192 | * @return 影响因数 193 | */ 194 | double factor() default Double.MIN_VALUE; 195 | 196 | /** 197 | * 指定重试的等待时间 class 信息 198 | * @return 重试等待时间 class 199 | */ 200 | Class retryWait() default NoRetryWait.class; 201 | 202 | } 203 | 204 | ``` 205 | 206 | 207 | 208 | ### 注解解析流程 209 | 210 | 211 | 212 | 213 | 214 | ## 模块介绍 215 | 216 | ### retry-api 217 | 218 | * 接口定义模块 219 | 220 | * 参考Netty的接口设计,如果想实现自己的重试框架,可以引入尝试一下 221 | 222 | ### retry-core 223 | 224 | * 核心模块。是对retry-api模块的默认实现 225 | * 拥有`Retryer`引导类,支持用优雅的 **Fluent API** 写出声明式的重试代码 226 | 227 | ### retry-annotation 228 | 229 | * 注解实现模块 230 | * 基于动态代理或者CGLIB字节码实现的代理重试,不依赖spring,灵活使用 231 | * 允许自定义注解及其实现。用户可以模仿`com.poldroc.retry.annotation.annotation.Retry`,通过`@RetryAble()`包装自定义的`RetryAbleHandler`实现类来实现属于自己的重试注解 232 | 233 | ### retry-spring 234 | 235 | * 为Spring框架集成重试功能的模块,允许开发者在Spring应用中无缝使用声明式重试功能,它通过Spring AOP集成了重试机制 236 | 237 | * #### 主要组件 238 | 239 | 1. **`@EnableRetry` 注解**: 240 | 这是一个用于启用Spring重试功能的注解,类似于其他Spring功能的启用注解(如`@EnableCaching`)。它通过`@Import`将`RetryAopConfig`类注入到Spring的应用上下文中,从而启用AOP切面处理 241 | 2. **`RetryAop` 切面类**: 242 | `RetryAop`类负责拦截所有标注了`@Retry`注解的方法,并在方法执行失败时执行重试逻辑。该切面通过获取方法签名、参数等信息,调用重试处理器`RetryMethodHandler`进行实际的重试操作 243 | 3. **`RetryAopConfig` 配置类**: 244 | 这是一个Spring配置类,通过`@ComponentScan`自动扫描并注册spring模块中所有必要的组件(如RetryAop类),确保Spring可以正确管理这些组件 245 | 246 | ### retry-springboot-stater 247 | 248 | * 通过Spring Boot自动配置机制为项目提供开箱即用的重试功能 249 | * 该模板引入了`retry-spring` 模块,通过自动配置机制来使用`@EnableRetry` 注解,自动启用`retry-spring`模块中定义的重试功能。 250 | 251 | 252 | 253 | ## Support 254 | 255 | ### Condition 256 | 257 | 重试触发的条件,可以指定多个条件。默认为抛出异常。 258 | 259 | 基于[RetryCondition](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/condition/RetryCondition.java)接口,配合[stop](#Stop)使用,当符合condition并且不符合stop,则开始重试。 260 | 261 | `RetryCondition`接口定义如下: 262 | 263 | ```java 264 | public interface RetryCondition { 265 | boolean condition(final RetryAttempt retryAttempt); 266 | } 267 | ``` 268 | 269 | `RetryAttempt`定义如下: 270 | 271 | ```java 272 | public interface RetryAttempt { 273 | 274 | /** 275 | * 获取方法执行的结果 276 | * @return 方法执行的结果 277 | */ 278 | R result(); 279 | 280 | /** 281 | * 获取重试次数 282 | * @return 重试次数 283 | */ 284 | int attempt(); 285 | 286 | /** 287 | * 获取异常信息 288 | * @return 异常信息 289 | */ 290 | Throwable cause(); 291 | 292 | /** 293 | * 获取消耗时间 294 | * @return 消耗时间 295 | */ 296 | AttemptTime time(); 297 | 298 | /** 299 | * 获取重试历史信息 300 | * @return 重试历史信息 301 | */ 302 | List> history(); 303 | 304 | /** 305 | * 获取请求参数 306 | * @return 请求参数 307 | */ 308 | Object[] params(); 309 | } 310 | ``` 311 | 312 | `RetryCondition`基于[RetryAttempt](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/model/RetryAttempt.java)内容来自定义重试的条件,其属性包含`方法执行的结果`、`重试次数`、`异常`、`重试历史信息`、`请求参数`。所以可以通过这些属性来自定义重试条件。`RetryAttempt`每次execute重试会更新,因此每次判断是否重试的时候可以获取上次重试信息。 313 | 314 | #### 用户自定义 315 | 316 | 用户可以继承不同的抽象类来实现自定义的重试条件判断: 317 | 318 | 1. **继承 AbstractCauseRetryCondition** 并重载 `causeCondition` 方法,通过异常信息判断是否触发重试。 319 | 2. **继承 AbstractResultRetryCondition** 并重载 `resultCondition` 方法,根据结果判断是否触发重试。 320 | 3. **继承 AbstractTimeRetryCondition** 并重载 `timeCondition` 方法,通过消耗时间判断是否触发重试。 321 | 322 | ### Stop 323 | 324 | 终止重试的条件。默认为重试次数为3,包括第一次执行。 325 | 326 | 基于[RetryStop](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/stop/RetryStop.java)接口,配合[condition](#Condition)使用,当符合condition并且不符合stop,则开始重试。 327 | 328 | `RetryStop`定义如下: 329 | 330 | ```java 331 | public interface RetryStop { 332 | boolean stop(final RetryAttempt attempt); 333 | } 334 | ``` 335 | 336 | `RetryStop`基于[RetryAttempt](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/model/RetryAttempt.java)内容来自定义终止的条件,其属性包含`方法执行的结果`、`重试次数`、`异常`、`重试历史信息`、`请求参数`。所以可以通过这些属性来自定义终止条件。`RetryAttempt`每次execute重试会更新,因此每次判断是否终止的时候可以获取上次重试信息。 337 | 338 | #### 用户自定义 339 | 340 | 用户可以实现`RetryStop`接口来实现自定义的终止条件判断,可以参考默认的终止策略: 341 | 342 | ```java 343 | public class MaxAttemptRetryStop implements RetryStop { 344 | 345 | private final int maxAttempt; 346 | 347 | public MaxAttemptRetryStop(int maxAttempt) { 348 | if (maxAttempt <= 0) { 349 | throw new IllegalArgumentException("MaxAttempt must be positive"); 350 | } 351 | this.maxAttempt = maxAttempt; 352 | } 353 | 354 | @Override 355 | public boolean stop(RetryAttempt attempt) { 356 | return attempt.attempt() >= maxAttempt; 357 | } 358 | } 359 | ``` 360 | 361 | ### Wait 362 | 363 | 重试等待策略。默认为无时间等待(不建议使用)。 364 | 365 | 基于[RetryWait](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/wait/RetryWait.java)接口,接口定义如下: 366 | 367 | ```java 368 | public interface RetryWait{ 369 | WaitTime waitTime(final RetryWaitContext retryWaitContext); 370 | } 371 | ``` 372 | 373 | `RetryWaitContext`定义如下: 374 | 375 | ```java 376 | public interface RetryWaitContext extends RetryAttempt { 377 | /** 378 | * 基础值(毫秒) 379 | * 1. fixed: 固定间隔 380 | * 2. 递增/指数:为初始值 381 | * 3. random/noRetry 这个值会被忽略 382 | * @return 基础值 383 | */ 384 | long value(); 385 | 386 | /** 387 | * 最小等待时间(毫秒) 388 | * @return 最小等待时间(毫秒) 389 | */ 390 | long min(); 391 | 392 | /** 393 | * 最大等待时间(毫秒) 394 | * @return 最大等待时间(毫秒) 395 | */ 396 | long max(); 397 | 398 | /** 399 | * 变换因子(递增/毫秒) 400 | * 1. 递增:每次增加的时间 401 | * 2. 指数:每次乘的因数 402 | * @return 变换因子 403 | */ 404 | double factor(); 405 | 406 | /** 407 | * 对应的 class 信息 408 | * @return class 信息 409 | */ 410 | Class retryWait(); 411 | 412 | } 413 | ``` 414 | 415 | 通过策略计算出重试之间的等待时间,配合[block](#Block)实现重试等待。 416 | 417 | 组件提供了四种等待时间计算策略: 418 | 419 | 1. `NoRetryWait`:无时间等待策略,立即重试。 420 | 2. `FixedRetryWait`:固定时间间隔等待策略。 421 | 3. `IncreaseRetryWait`:递增重试等待策略,根据重试次数,等待时间呈factor常数增长。 422 | 4. `ExponentialRetryWait`:指数增长的重试等待策略,根据重试次数,等待时间呈factor指数增长。 423 | 424 | 在源码中,`RetryWait`的使用是需要`RetryWaiter`构造器来构建重试等待时间上下文信息`RetryWaitContext`。 425 | 426 | 在`RetryWaiter`构造器中,默认等待时间`value`为1s、最小时间值`min`为0s、最大时间值`max`为30min、变化因子`factor`: 如果策略选择`ExponentialRetryWait`指数增长的重试等待策略,默认值为1.618;选择`IncreaseRetryWait`递增重试等待策略,默认值为2s。 427 | 428 | ```java 429 | public class RetryWaiter { 430 | 431 | /** 432 | * 重试等待类的类型 433 | */ 434 | private Class retryWait = NoRetryWait.class; 435 | 436 | /** 437 | * 默认的等待时间 438 | */ 439 | private long value = RetryWaitConst.DEFAULT_VALUE_MILLS; 440 | 441 | /** 442 | * 最小值 443 | */ 444 | private long min = RetryWaitConst.DEFAULT_MIN_MILLS; 445 | 446 | /** 447 | * 最大值 448 | */ 449 | private long max = RetryWaitConst.DEFAULT_MAX_MILLS; 450 | 451 | /** 452 | * 变化因子 453 | *

454 | * 1. 如果是 {@link com.poldroc.retry.core.support.wait.ExponentialRetryWait} 则为 {@link com.poldroc.retry.core.constant.RetryWaitConst#MULTIPLY_FACTOR} 455 | *

456 | * 2. 如果是 {@link com.poldroc.retry.core.support.wait.IncreaseRetryWait} 则为 {@link com.poldroc.retry.core.constant.RetryWaitConst#INCREASE_MILLS_FACTOR} 457 | */ 458 | private double factor = Double.MIN_VALUE; 459 | 460 | /** 461 | * 构造器私有化 462 | */ 463 | private RetryWaiter() { 464 | } 465 | 466 | /** 467 | * 设置重试等待的对象类型 468 | * 并且设置默认的因子 469 | * @param retryWait 重试等待类 470 | * @param 泛型 471 | * @return 重试等待类 472 | */ 473 | public static RetryWaiter retryWait(Class retryWait) { 474 | RetryWaiter retryWaiter = new RetryWaiter<>(); 475 | retryWaiter.retryWait = retryWait; 476 | if (IncreaseRetryWait.class.equals(retryWait)) { 477 | retryWaiter.factor(RetryWaitConst.INCREASE_MILLS_FACTOR); 478 | } 479 | if (ExponentialRetryWait.class.equals(retryWait)) { 480 | retryWaiter.factor(RetryWaitConst.MULTIPLY_FACTOR); 481 | } 482 | return retryWaiter; 483 | } 484 | 485 | public Class retryWait() { 486 | return retryWait; 487 | } 488 | 489 | public long value() { 490 | return value; 491 | } 492 | 493 | public RetryWaiter value(long value) { 494 | this.value = value; 495 | return this; 496 | } 497 | 498 | public long min() { 499 | return min; 500 | } 501 | 502 | public RetryWaiter min(long min) { 503 | this.min = min; 504 | return this; 505 | } 506 | 507 | public long max() { 508 | return max; 509 | } 510 | 511 | public RetryWaiter max(long max) { 512 | this.max = max; 513 | return this; 514 | } 515 | 516 | public double factor() { 517 | return factor; 518 | } 519 | 520 | public RetryWaiter factor(double factor) { 521 | this.factor = factor; 522 | return this; 523 | } 524 | 525 | /** 526 | * 构建重试等待时间上下文 527 | */ 528 | public RetryWaitContext context() { 529 | return new DefaultRetryWaitContext() 530 | .value(value) 531 | .min(min) 532 | .max(max) 533 | .factor(factor) 534 | .retryWait(retryWait); 535 | } 536 | } 537 | ``` 538 | 539 | 540 | 541 | #### 用户自定义 542 | 543 | 用户可以继承`AbstractRetryWait`抽象类来实现自定义的重试等待时间计算策略,如: 544 | 545 | ```java 546 | /** 547 | * 递增重试等待策略 548 | */ 549 | public class IncreaseRetryWait extends AbstractRetryWait { 550 | @Override 551 | public WaitTime waitTime(RetryWaitContext retryWaitContext) { 552 | int previousAttempt = retryWaitContext.attempt() - 1; 553 | // 结果为重试等待时间的值加上重试次数减一乘以重试等待时间的因子,然后四舍五入 554 | long result = Math.round(retryWaitContext.value() + previousAttempt * retryWaitContext.factor()); 555 | return super.rangeCorrect(result, retryWaitContext.min(), retryWaitContext.max()); 556 | } 557 | } 558 | 559 | ``` 560 | 561 | 562 | 563 | ### Block 564 | 565 | 重试等待阻塞方式。默认为线程睡眠的阻塞方法。 566 | 567 | 基于[RetryBlock](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/block/RetryBlock.java)接口,定义如下: 568 | 569 | ```java 570 | public interface RetryBlock { 571 | void block(final WaitTime waitTime); 572 | } 573 | ``` 574 | 575 | #### 用户自定义 576 | 577 | 用户可以实现`RetryBlock`接口来实现自定义的阻塞策略,可以参考默认的阻塞策略: 578 | 579 | ```java 580 | public class ThreadSleepRetryBlock implements RetryBlock { 581 | @Override 582 | public void block(WaitTime waitTime) { 583 | try { 584 | waitTime.unit().sleep(waitTime.time()); 585 | } catch (InterruptedException e) { 586 | // 恢复状态 587 | Thread.currentThread().interrupt(); 588 | throw new RetryException(e); 589 | } 590 | } 591 | } 592 | ``` 593 | 594 | 595 | 596 | ### Listen 597 | 598 | 指定重试的监听实现,默认为不做监听。 599 | 600 | 基于[RetryListen](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/listen/RetryListen.java)接口,接口定义如下: 601 | 602 | ```java 603 | public interface RetryListen { 604 | void listen(final RetryAttempt attempt); 605 | } 606 | ``` 607 | 608 | 通过抽象化监听器`AbstractRetryListenInit`,使得不同的监听行为可以被灵活组合。 609 | 610 | ```java 611 | public abstract class AbstractRetryListenInit implements RetryListen { 612 | @Override 613 | public void listen(RetryAttempt attempt) { 614 | List listens = new LinkedList<>(); 615 | this.init(listens, attempt); 616 | // 执行 617 | for (RetryListen listen : listens) { 618 | listen.listen(attempt); 619 | } 620 | } 621 | 622 | protected abstract void init(final LinkedList pipeline, final RetryAttempt attempt); 623 | } 624 | ``` 625 | 626 | * **监听器链执行**:它实现了一个通用的 `listen()` 方法,管理和执行多个监听器。 627 | 628 | * **抽象初始化**:通过抽象方法 `init()`,将具体的监听器添加过程交给子类去实现,从而实现不同的监听器初始化逻辑。 629 | 630 | 当需要同时执行多个监听器时,比如同时记录日志和统计重试次数,可以通过 `RetryListens.listens()` 组合它们,简化代码中的监听器管理。定义如下: 631 | 632 | ```java 633 | public class RetryListens { 634 | 635 | private RetryListens() { 636 | } 637 | 638 | /** 639 | * 不进行任何监听动作 640 | * 641 | * @return 监听器 642 | */ 643 | public static RetryListen noListen() { 644 | return NoRetryListen.getInstance(); 645 | } 646 | 647 | /** 648 | * 指定多个监听器 649 | * 650 | * @param retryListens 多个监听器信息 651 | * @return 监听器 652 | */ 653 | public static RetryListen listens(final RetryListen... retryListens) { 654 | if (null == retryListens || retryListens.length == 0) { 655 | return noListen(); 656 | } 657 | return new AbstractRetryListenInit() { 658 | @Override 659 | protected void init(LinkedList pipeline, RetryAttempt attempt) { 660 | for (RetryListen retryListen : retryListens) { 661 | pipeline.addLast(retryListen); 662 | } 663 | } 664 | }; 665 | } 666 | } 667 | ``` 668 | 669 | #### 用户自定义 670 | 671 | 1. 当需要执行多个监听器时,`AbstractRetryListenInit` 提供了基础的执行逻辑。开发者可以通过继承该类并实现 `init()` 方法,来实现自定义的监听器初始化和执行顺序。 672 | 673 | ```java 674 | public class CustomRetryListen extends AbstractRetryListenInit { 675 | @Override 676 | protected void init(LinkedList pipeline, RetryAttempt attempt) { 677 | // 自定义初始化逻辑,比如根据重试次数决定是否添加某个监听器 678 | if (attempt.attempt() == 2) { 679 | pipeline.add(new LogRetryListen()); 680 | } 681 | pipeline.add(new StatRetryListen()); 682 | } 683 | 684 | private class LogRetryListen implements RetryListen { 685 | 686 | @Override 687 | public void listen(RetryAttempt attempt) { 688 | System.out.println("LogRetryListen: " + attempt); 689 | } 690 | } 691 | 692 | private class StatRetryListen implements RetryListen { 693 | @Override 694 | public void listen(RetryAttempt attempt) { 695 | System.out.println("StatRetryListen"); 696 | } 697 | } 698 | } 699 | ``` 700 | 701 | 2. 也可以实现`RetryListen`接口,通过调用 `RetryListens.listens()` 方法,将多个监听器 (`MyListen1`、`MyListen2`、`MyListen3`) 组合成一个执行链,在重试机制中执行这些监听器。 702 | 703 | ```java 704 | public class MyListens implements RetryListen { 705 | 706 | @Override 707 | public void listen(RetryAttempt attempt) { 708 | RetryListens.listens(new MyListen1(), new MyListen2(), new MyListen3()).listen(attempt); 709 | } 710 | } 711 | ``` 712 | 713 | 714 | 715 | ### Recover 716 | 717 | 当仍然满足重试条件,但是满足重试停止条件时,则可以触发指定恢复的策略。默认不做恢复。 718 | 719 | 基于[Recover](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/recover/Recover.java)接口,接口定义如下: 720 | 721 | ```java 722 | public interface Recover { 723 | 724 | /** 725 | * 执行恢复 726 | * @param retryAttempt 重试信息 727 | * @param 泛型 728 | */ 729 | void recover(final RetryAttempt retryAttempt); 730 | 731 | } 732 | ``` 733 | 734 | 735 | 736 | #### 用户自定义 737 | 738 | 用户可以实现`Recover`接口来实现自定义的恢复策略,可以参考默认的恢复策略: 739 | 740 | ```java 741 | public class MyRecover implements Recover { 742 | 743 | @Override 744 | public void recover(RetryAttempt retryAttempt) { 745 | Object[] params = retryAttempt.params(); 746 | 747 | String name = params[0].toString(); 748 | // 通知 749 | System.out.println("[Recover] " + name + "查询失败了!"); 750 | } 751 | } 752 | ``` 753 | 754 | 755 | 756 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # roc-retry 2 | [中文](./README-zh.md) 3 | 4 | roc-retry is a Java retry framework that supports both procedural programming and annotation-based programming. 5 | 6 | ## **Feature Description** 7 | 8 | * Uses the **Builder pattern**, supporting an elegant **Fluent API** programming style 9 | * Based on CGLIB bytecode proxy retry 10 | * Annotation-based retry mechanism, allowing users to customize annotations 11 | * Provides **multiple support strategies**, including blocking, listening, recovery, waiting, and stopping strategies 12 | * Adopts Netty-like interface **API design** philosophy, ensuring interface consistency and flexibility in replacement 13 | * Seamless integration with Spring/Spring-Boot 14 | 15 | 16 | 17 | ## Quick Start 18 | 19 | ### Introduction 20 | 21 | ```xml 22 | 23 | io.github.poldroc 24 | retry-core 25 | 1.1 26 | 27 | ``` 28 | 29 | 30 | 31 | ### Simple Entry 32 | 33 | ```java 34 | public class RetryerTest { 35 | /** 36 | * Retry on default exception 37 | */ 38 | @Test(expected = RuntimeException.class) 39 | public void helloTest() { 40 | Retryer.newInstance() 41 | .callable(new Callable() { 42 | @Override 43 | public String call() throws Exception { 44 | System.out.println("called..."); 45 | throw new RuntimeException(); 46 | } 47 | }).retryCall(); 48 | } 49 | 50 | /** 51 | * Default configuration test 52 | */ 53 | @Test(expected = RuntimeException.class) 54 | public void defaultConfigTest() { 55 | Retryer.newInstance() 56 | .maxAttempt(4) 57 | .listen(RetryListens.noListen()) 58 | .recover(Recovers.noRecover()) 59 | .condition(RetryConditions.hasExceptionCause()) 60 | .retryWaitContext(RetryWaiter.retryWait(NoRetryWait.class).context()) 61 | .callable(new Callable() { 62 | @Override 63 | public String call() throws Exception { 64 | System.out.println("called..."); 65 | throw new RuntimeException(); 66 | } 67 | }).retryCall(); 68 | } 69 | } 70 | ``` 71 | 72 | 73 | 74 | ## Annotation Usage 75 | 76 | ### Introduction 77 | 78 | ```xml 79 | 80 | io.github.poldroc 81 | retry-annotation 82 | 1.1 83 | 84 | ``` 85 | 86 | [Usage Details](https://github.com/Poldroc/roc-retry/blob/master/retry-test/src/test/java/com/poldroc/retry/test/springboot/RocRetryApplicationTest.java) 87 | 88 | ### Retry 89 | 90 | Used to specify retry-related configurations 91 | 92 | ```java 93 | @Documented 94 | @Inherited 95 | @Target(ElementType.METHOD) 96 | @Retention(RetentionPolicy.RUNTIME) 97 | @RetryAble(DefaultRetryAbleHandler.class) 98 | public @interface Retry { 99 | 100 | /** 101 | * Retry class implementation 102 | * 103 | * @return Retry 104 | */ 105 | Class retry() default DefaultRetry.class; 106 | 107 | /** 108 | * Maximum number of attempts 109 | * 1. Default is 3 including the first normal execution of the method 110 | * 111 | * @return Number of attempts 112 | */ 113 | int maxAttempt() default 3; 114 | 115 | /** 116 | * Scenario that triggers a retry 117 | * 1. Default is triggered by exception 118 | * 119 | * @return Scenario that triggers a retry 120 | */ 121 | Class condition() default ExceptionCauseRetryCondition.class; 122 | 123 | /** 124 | * Listener 125 | * 1. Default does not perform any listening 126 | * 127 | * @return Listener 128 | */ 129 | Class listen() default NoRetryListen.class; 130 | 131 | /** 132 | * Recovery operation 133 | * 1. Default does not perform any recovery operation 134 | * 135 | * @return Class corresponding to the recovery operation 136 | */ 137 | Class recover() default NoRecover.class; 138 | 139 | /** 140 | * Retry wait strategy 141 | * 1. Supports specifying multiple, if not specified, no waiting is performed, 142 | * 143 | * @return Retry wait strategy 144 | */ 145 | RetryWait[] waits() default {}; 146 | } 147 | 148 | ``` 149 | 150 | 151 | 152 | ### RetryWait 153 | 154 | Used to specify the retry wait strategy 155 | 156 | ```java 157 | @Retention(RetentionPolicy.RUNTIME) 158 | @Inherited 159 | @Documented 160 | @Target(ElementType.ANNOTATION_TYPE) 161 | @RetryWaitAble(DefaultRetryWaitAbleHandler.class) 162 | public @interface RetryWait { 163 | 164 | /** 165 | * Default value 166 | * 1. fixed mode, corresponds to a fixed wait time 167 | * 2. Incremental 168 | * @return Default value 169 | */ 170 | long value() default RetryWaitConst.DEFAULT_VALUE_MILLS; 171 | 172 | /** 173 | * Minimum value 174 | * @return Minimum value 175 | */ 176 | long min() default RetryWaitConst.DEFAULT_MIN_MILLS; 177 | 178 | /** 179 | * Maximum value 180 | * @return Maximum value 181 | */ 182 | long max() default RetryWaitConst.DEFAULT_MAX_MILLS; 183 | 184 | /** 185 | * Influencing factor 186 | * 1. Incremental retry, default is {@link RetryWaitConst#INCREASE_MILLS_FACTOR} 187 | * 2. Exponential mode. Default is {@link RetryWaitConst#MULTIPLY_FACTOR} 188 | * @return Influencing factor 189 | */ 190 | double factor() default Double.MIN_VALUE; 191 | 192 | /** 193 | * Specifies the class information for the retry wait time 194 | * @return Retry wait time class 195 | */ 196 | Class retryWait() default NoRetryWait.class; 197 | 198 | } 199 | 200 | ``` 201 | 202 | 203 | 204 | ### Annotation Parsing Process 205 | 206 | 207 | 208 | 209 | 210 | ## Module Introduction 211 | 212 | ### retry-api 213 | 214 | * Interface definition module 215 | 216 | * Refers to Netty's interface design, if you want to implement your own retry framework, you can try to introduce it 217 | 218 | ### retry-core 219 | 220 | * Core module. It is the default implementation of the retry-api module 221 | * Has the `Retryer` bootstrap class, which supports writing declarative retry code with an elegant **Fluent API** 222 | 223 | ### retry-annotation 224 | 225 | * Annotation implementation module 226 | * Proxy retry based on dynamic proxy or CGLIB bytecode, not dependent on spring, flexible use 227 | * Allows customization of annotations and their implementations. Users can imitate `com.poldroc.retry.annotation.annotation.Retry`, by `@RetryAble()` wrapping custom `RetryAbleHandler` implementation classes to achieve their own retry annotations 228 | 229 | ### retry-spring 230 | 231 | * Module that integrates retry functionality into the Spring framework, allowing developers to seamlessly use declarative retry functionality in Spring applications, it integrates the retry mechanism through Spring AOP 232 | 233 | * #### Main Components 234 | 235 | 1. **`@EnableRetry` Annotation**: 236 | This is an annotation used to enable Spring retry functionality, similar to other Spring functionality enabling annotations (such as `@EnableCaching`). It injects the `RetryAopConfig` class into the Spring application context through `@Import`, thereby enabling AOP aspect processing 237 | 2. **`RetryAop` Aspect Class**: 238 | The `RetryAop` class is responsible for intercepting all methods marked with the `@Retry` annotation and executing the retry logic when the method execution fails. This aspect obtains method signatures, parameters, and other information, and calls the retry processor `RetryMethodHandler` to perform the actual retry operation 239 | 3. **`RetryAopConfig` Configuration Class**: 240 | This is a Spring configuration class that automatically scans and registers all necessary components in the spring module (such as the RetryAop class) through `@ComponentScan`, ensuring that Spring can correctly manage these components 241 | 242 | ### retry-springboot-stater 243 | 244 | * Provides out-of-the-box retry functionality for projects through the Spring Boot auto-configuration mechanism 245 | * This template introduces the `retry-spring` module, which uses the auto-configuration mechanism to use the `@EnableRetry` annotation and automatically enables the retry functionality defined in the `retry-spring` module. 246 | 247 | 248 | 249 | ## Support 250 | 251 | ### Condition 252 | 253 | The condition for triggering a retry can specify multiple conditions. Default is to throw an exception. 254 | 255 | Based on the [RetryCondition](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/condition/RetryCondition.java) interface, used in conjunction with [stop](#Stop), when it meets the condition and does not meet the stop, it starts to retry. 256 | 257 | The `RetryCondition` interface is defined as follows: 258 | 259 | ```java 260 | public interface RetryCondition { 261 | boolean condition(final RetryAttempt retryAttempt); 262 | } 263 | ``` 264 | 265 | `RetryAttempt` is defined as follows: 266 | 267 | ```java 268 | public interface RetryAttempt { 269 | 270 | /** 271 | * Get the result of the method execution 272 | * @return The result of the method execution 273 | */ 274 | R result(); 275 | 276 | /** 277 | * Get the number of retries 278 | * @return The number of retries 279 | */ 280 | int attempt(); 281 | 282 | /** 283 | * Get the exception information 284 | * @return The exception information 285 | */ 286 | Throwable cause(); 287 | 288 | /** 289 | * Get the time consumed 290 | * @return The time consumed 291 | */ 292 | AttemptTime time(); 293 | 294 | /** 295 | * Get the retry history information 296 | * @return The retry history information 297 | */ 298 | List> history(); 299 | 300 | /** 301 | * Get the request parameters 302 | * @return The request parameters 303 | */ 304 | Object[] params(); 305 | } 306 | ``` 307 | 308 | `RetryCondition` is based on [RetryAttempt](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/model/RetryAttempt.java) content to customize the retry condition, its properties include `method execution result`, `number of retries`, `exception`, `retry history information`, `request parameters`. So you can customize the retry condition through these properties. `RetryAttempt` is updated every time execute retries, so you can get the last retry information every time you decide whether to retry. 309 | 310 | #### Customization by Users 311 | 312 | Users can inherit different abstract classes to implement custom retry condition judgments: 313 | 314 | 1. **Inherit AbstractCauseRetryCondition** and override the `causeCondition` method to judge whether to trigger a retry through exception information. 315 | 2. **Inherit AbstractResultRetryCondition** and override the `resultCondition` method to judge whether to trigger a retry based on the result. 316 | 3. **Inherit AbstractTimeRetryCondition** and override the `timeCondition` method to judge whether to trigger a retry through the time consumed. 317 | 318 | ### Stop 319 | 320 | The condition for terminating a retry. Default is 3 retry attempts, including the first execution. 321 | 322 | Based on the [RetryStop](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/stop/RetryStop.java) interface, used in conjunction with [condition](#Condition), when it meets the condition and does not meet the stop, it starts to retry. 323 | 324 | The `RetryStop` is defined as follows: 325 | 326 | ```java 327 | public interface RetryStop { 328 | boolean stop(final RetryAttempt attempt); 329 | } 330 | ``` 331 | 332 | `RetryStop` is based on [RetryAttempt](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/model/RetryAttempt.java) content to customize the termination condition, its properties include `method execution result`, `number of retries`, `exception`, `retry history information`, `request parameters`. So you can customize the termination condition through these properties. `RetryAttempt` is updated every time execute retries, so you can get the last retry information every time you decide whether to terminate. 333 | 334 | #### Customization by Users 335 | 336 | Users can implement the `RetryStop` interface to implement custom termination condition judgments, you can refer to the default termination strategy: 337 | 338 | ```java 339 | public class MaxAttemptRetryStop implements RetryStop { 340 | 341 | private final int maxAttempt; 342 | 343 | public MaxAttemptRetryStop(int maxAttempt) { 344 | if (maxAttempt <= 0) { 345 | throw new IllegalArgumentException("MaxAttempt must be positive"); 346 | } 347 | this.maxAttempt = maxAttempt; 348 | } 349 | 350 | @Override 351 | public boolean stop(RetryAttempt attempt) { 352 | return attempt.attempt() >= maxAttempt; 353 | } 354 | } 355 | ``` 356 | 357 | ### Wait 358 | 359 | Retry wait strategy. Default is no waiting time (not recommended). 360 | 361 | Based on the [RetryWait](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/wait/RetryWait.java) interface, the interface is defined as follows: 362 | 363 | ```java 364 | public interface RetryWait{ 365 | WaitTime waitTime(final RetryWaitContext retryWaitContext); 366 | } 367 | ``` 368 | 369 | `RetryWaitContext` is defined as follows: 370 | 371 | ```java 372 | public interface RetryWaitContext extends RetryAttempt { 373 | /** 374 | * Base value (milliseconds) 375 | * 1. fixed: fixed interval 376 | * 2. Incremental/Exponential: as the initial value 377 | * 3. random/noRetry this value will be ignored 378 | * @return Base value 379 | */ 380 | long value(); 381 | 382 | /** 383 | * Minimum wait time (milliseconds) 384 | * @return Minimum wait time (milliseconds) 385 | */ 386 | long min(); 387 | 388 | /** 389 | * Maximum wait time (milliseconds) 390 | * @return Maximum wait time (milliseconds) 391 | */ 392 | long max(); 393 | 394 | /** 395 | * Transformation factor (Incremental/Milliseconds) 396 | * 1. Incremental: the time increased each time 397 | * 2. Exponential: the factor multiplied each time 398 | * @return Transformation factor 399 | */ 400 | double factor(); 401 | 402 | /** 403 | * Corresponding class information 404 | * @return Class information 405 | */ 406 | Class retryWait(); 407 | 408 | } 409 | ``` 410 | 411 | Calculate the wait time between retries through the strategy, and implement retry waiting with [block](#Block). 412 | 413 | The component provides four wait time calculation strategies: 414 | 415 | 1. `NoRetryWait`: No waiting time strategy, retry immediately. 416 | 2. `FixedRetryWait`: Fixed time interval waiting strategy. 417 | 3. `IncreaseRetryWait`: Incremental retry waiting strategy, according to the number of retries, the wait time increases by a constant factor. 418 | 4. `ExponentialRetryWait`: Exponential growth retry waiting strategy, according to the number of retries, the wait time grows exponentially by a factor. 419 | 420 | In the source code, the use of `RetryWait` requires the `RetryWaiter` constructor to build the retry wait time context information `RetryWaitContext`. 421 | 422 | In the `RetryWaiter` constructor, the default wait time `value` is 1s, the minimum time value `min` is 0s, the maximum time value `max` is 30min, and the change factor `factor`: if the strategy chooses `ExponentialRetryWait` exponential growth retry waiting strategy, the default value is 1.618; if `IncreaseRetryWait` incremental retry waiting strategy is chosen, the default value is 2s. 423 | 424 | ```java 425 | public class RetryWaiter { 426 | 427 | /** 428 | * Type of retry wait class 429 | */ 430 | private Class retryWait = NoRetryWait.class; 431 | 432 | /** 433 | * Default wait time 434 | */ 435 | private long value = RetryWaitConst.DEFAULT_VALUE_MILLS; 436 | 437 | /** 438 | * Minimum value 439 | */ 440 | private long min = RetryWaitConst.DEFAULT_MIN_MILLS; 441 | 442 | /** 443 | * Maximum value 444 | */ 445 | private long max = RetryWaitConst.DEFAULT_MAX_MILLS; 446 | 447 | /** 448 | * Change factor 449 | *

450 | * 1. If it is {@link com.poldroc.retry.core.support.wait.ExponentialRetryWait} then it is {@link com.poldroc.retry.core.constant.RetryWaitConst#MULTIPLY_FACTOR} 451 | *

452 | * 2. If it is {@link com.poldroc.retry.core.support.wait.IncreaseRetryWait} then it is {@link com.poldroc.retry.core.constant.RetryWaitConst#INCREASE_MILLS_FACTOR} 453 | */ 454 | private double factor = Double.MIN_VALUE; 455 | 456 | /** 457 | * Private constructor 458 | */ 459 | private RetryWaiter() { 460 | } 461 | 462 | /** 463 | * Set the type of retry wait object 464 | * And set the default factor 465 | * @param retryWait Retry wait class 466 | * @param Generic 467 | * @return Retry wait class 468 | */ 469 | public static RetryWaiter retryWait(Class retryWait) { 470 | RetryWaiter retryWaiter = new RetryWaiter<>(); 471 | retryWaiter.retryWait = retryWait; 472 | if (IncreaseRetryWait.class.equals(retryWait)) { 473 | retryWaiter.factor(RetryWaitConst.INCREASE_MILLS_FACTOR); 474 | } 475 | if (ExponentialRetryWait.class.equals(retryWait)) { 476 | retryWaiter.factor(RetryWaitConst.MULTIPLY_FACTOR); 477 | } 478 | return retryWaiter; 479 | } 480 | 481 | public Class retryWait() { 482 | return retryWait; 483 | } 484 | 485 | public long value() { 486 | return value; 487 | } 488 | 489 | public RetryWaiter value(long value) { 490 | this.value = value; 491 | return this; 492 | } 493 | 494 | public long min() { 495 | return min; 496 | } 497 | 498 | public RetryWaiter min(long min) { 499 | this.min = min; 500 | return this; 501 | } 502 | 503 | public long max() { 504 | return max; 505 | } 506 | 507 | public RetryWaiter max(long max) { 508 | this.max = max; 509 | return this; 510 | } 511 | 512 | public double factor() { 513 | return factor; 514 | } 515 | 516 | public RetryWaiter factor(double factor) { 517 | this.factor = factor; 518 | return this; 519 | } 520 | 521 | /** 522 | * Build retry wait time context 523 | */ 524 | public RetryWaitContext context() { 525 | return new DefaultRetryWaitContext() 526 | .value(value) 527 | .min(min) 528 | .max(max) 529 | .factor(factor) 530 | .retryWait(retryWait); 531 | } 532 | } 533 | ``` 534 | 535 | 536 | 537 | #### Customization by Users 538 | 539 | Users can inherit the `AbstractRetryWait` abstract class to implement custom retry wait time calculation strategies, such as: 540 | 541 | ```java 542 | /** 543 | * Incremental retry wait strategy 544 | */ 545 | public class IncreaseRetryWait extends AbstractRetryWait { 546 | @Override 547 | public WaitTime waitTime(RetryWaitContext retryWaitContext) { 548 | int previousAttempt = retryWaitContext.attempt() - 1; 549 | // The result is the value of the retry wait time plus the retry attempt minus one multiplied by the retry wait time factor, then rounded 550 | long result = Math.round(retryWaitContext.value() + previousAttempt * retryWaitContext.factor()); 551 | return super.rangeCorrect(result, retryWaitContext.min(), retryWaitContext.max()); 552 | } 553 | } 554 | 555 | ``` 556 | 557 | 558 | 559 | ### Block 560 | 561 | The way to block retry waiting. Default is thread sleep blocking method. 562 | 563 | Based on the [RetryBlock](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/block/RetryBlock.java) interface, defined as follows: 564 | 565 | ```java 566 | public interface RetryBlock { 567 | void block(final WaitTime waitTime); 568 | } 569 | ``` 570 | 571 | #### Customization by Users 572 | 573 | Users can implement the `RetryBlock` interface to implement custom blocking strategies, you can refer to the default blocking strategy: 574 | 575 | ```java 576 | public class ThreadSleepRetryBlock implements RetryBlock { 577 | @Override 578 | public void block(WaitTime waitTime) { 579 | try { 580 | waitTime.unit().sleep(waitTime.time()); 581 | } catch (InterruptedException e) { 582 | // Restore status 583 | Thread.currentThread().interrupt(); 584 | throw new RetryException(e); 585 | } 586 | } 587 | } 588 | ``` 589 | 590 | 591 | 592 | ### Listen 593 | 594 | Specify the implementation of the retry listener, default is no listening. 595 | 596 | Based on the [RetryListen](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/listen/RetryListen.java) interface, the interface is defined as follows: 597 | 598 | ```java 599 | public interface RetryListen { 600 | void listen(final RetryAttempt attempt); 601 | } 602 | ``` 603 | 604 | Through the abstract listener `AbstractRetryListenInit`, different listening behaviors can be flexibly combined. 605 | 606 | ```java 607 | public abstract class AbstractRetryListenInit implements RetryListen { 608 | @Override 609 | public void listen(RetryAttempt attempt) { 610 | List listens = new LinkedList<>(); 611 | this.init(listens, attempt); 612 | // Execute 613 | for (RetryListen listen : listens) { 614 | listen.listen(attempt); 615 | } 616 | } 617 | 618 | protected abstract void init(final LinkedList pipeline, final RetryAttempt attempt); 619 | } 620 | ``` 621 | 622 | * **Listener chain execution**: It implements a general `listen()` method, managing and executing multiple listeners. 623 | 624 | * **Abstract initialization**: By abstract method `init()`, the specific listener addition process is handed over to the subclass to implement, thereby achieving different listener initialization logic. 625 | 626 | When multiple listeners need to be executed at the same time, such as recording logs and counting retry attempts at the same time, you can combine them through `RetryListens.listens()`, simplifying the listener management in the code. It is defined as follows: 627 | 628 | ```java 629 | public class RetryListens { 630 | 631 | private RetryListens() { 632 | } 633 | 634 | /** 635 | * Do not perform any listening actions 636 | * 637 | * @return Listener 638 | */ 639 | public static RetryListen noListen() { 640 | return NoRetryListen.getInstance(); 641 | } 642 | 643 | /** 644 | * Specify multiple listeners 645 | * 646 | * @param retryListens Multiple listener information 647 | * @return Listener 648 | */ 649 | public static RetryListen listens(final RetryListen... retryListens) { 650 | if (null == retryListens || retryListens.length == 0) { 651 | return noListen(); 652 | } 653 | return new AbstractRetryListenInit() { 654 | @Override 655 | protected void init(LinkedList pipeline, RetryAttempt attempt) { 656 | for (RetryListen retryListen : retryListens) { 657 | pipeline.addLast(retryListen); 658 | } 659 | } 660 | }; 661 | } 662 | } 663 | ``` 664 | 665 | #### Customization by Users 666 | 667 | 1. When multiple listeners need to be executed, `AbstractRetryListenInit` provides the basic execution logic. Developers can inherit this class and implement the `init()` method to achieve custom listener initialization and execution order. 668 | 669 | ```java 670 | public class CustomRetryListen extends AbstractRetryListenInit { 671 | @Override 672 | protected void init(LinkedList pipeline, RetryAttempt attempt) { 673 | // Custom initialization logic, such as deciding whether to add a certain listener based on the number of retries 674 | if (attempt.attempt() == 2) { 675 | pipeline.add(new LogRetryListen()); 676 | } 677 | pipeline.add(new StatRetryListen()); 678 | } 679 | 680 | private class LogRetryListen implements RetryListen { 681 | 682 | @Override 683 | public void listen(RetryAttempt attempt) { 684 | System.out.println("LogRetryListen: " + attempt); 685 | } 686 | } 687 | 688 | private class StatRetryListen implements RetryListen { 689 | @Override 690 | public void listen(RetryAttempt attempt) { 691 | System.out.println("StatRetryListen"); 692 | } 693 | } 694 | } 695 | ``` 696 | 697 | 2. You can also implement the `RetryListen` interface, and by calling the `RetryListens.listens()` method, combine multiple listeners (`MyListen1`, `MyListen2`, `MyListen3`) into an execution chain, and execute these listeners in the retry mechanism. 698 | 699 | ```java 700 | public class MyListens implements RetryListen { 701 | 702 | @Override 703 | public void listen(RetryAttempt attempt) { 704 | RetryListens.listens(new MyListen1(), new MyListen2(), new MyListen3()).listen(attempt); 705 | } 706 | } 707 | ``` 708 | 709 | 710 | 711 | ### Recover 712 | 713 | When the retry conditions are still met, but the retry stop conditions are met, a specified recovery strategy can be triggered. Default is no recovery. 714 | 715 | Based on the [Recover](https://github.com/Poldroc/roc-retry/blob/master/retry-api/src/main/java/com/poldroc/retry/api/support/recover/Recover.java) interface, the interface is defined as follows: 716 | 717 | ```java 718 | public interface Recover { 719 | 720 | /** 721 | * Execute recovery 722 | * @param retryAttempt Retry information 723 | * @param Generic 724 | */ 725 | void recover(final RetryAttempt retryAttempt); 726 | 727 | } 728 | ``` 729 | 730 | 731 | 732 | #### Customization by Users 733 | 734 | Users can implement the `Recover` interface to implement custom recovery strategies, you can refer to the default recovery strategy: 735 | 736 | ```java 737 | public class MyRecover implements Recover { 738 | 739 | @Override 740 | public void recover(RetryAttempt retryAttempt) { 741 | Object[] params = retryAttempt.params(); 742 | 743 | String name = params[0].toString(); 744 | // Notification 745 | System.out.println("[Recover] " + name + " query failed!"); 746 | } 747 | } 748 | ``` 749 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.poldroc 8 | roc-retry 9 | 1.1 10 | roc-try 11 | The retry framework based on spring-retry and guava-retrying. 12 | pom 13 | 14 | retry-annotation 15 | retry-api 16 | retry-core 17 | retry-common 18 | retry-test 19 | retry-spring 20 | retry-springboot-starter 21 | 22 | 23 | 24 | 8 25 | 8 26 | UTF-8 27 | 28 | 4.13.1 29 | 3.1 30 | 5.3.20 31 | 1.9.19 32 | 2.7.5 33 | 34 | 35 | 36 | 37 | 38 | 39 | ${project.groupId} 40 | retry-api 41 | ${project.version} 42 | 43 | 44 | ${project.groupId} 45 | retry-core 46 | ${project.version} 47 | 48 | 49 | ${project.groupId} 50 | retry-annotation 51 | ${project.version} 52 | 53 | 54 | ${project.groupId} 55 | retry-common 56 | ${project.version} 57 | 58 | 59 | ${project.groupId} 60 | retry-spring 61 | ${project.version} 62 | 63 | 64 | ${project.groupId} 65 | retry-springboot-starter 66 | ${project.version} 67 | 68 | 69 | 70 | 71 | junit 72 | junit 73 | ${junit.version} 74 | test 75 | 76 | 77 | 78 | cglib 79 | cglib 80 | ${cglib.version} 81 | 82 | 83 | 84 | org.springframework 85 | spring-aop 86 | ${spring.version} 87 | 88 | 89 | org.springframework 90 | spring-context 91 | ${spring.version} 92 | 93 | 94 | org.springframework 95 | spring-test 96 | ${spring.version} 97 | test 98 | 99 | 100 | 101 | org.aspectj 102 | aspectjweaver 103 | ${aspectj.version} 104 | 105 | 106 | org.aspectj 107 | aspectjrt 108 | ${aspectj.version} 109 | 110 | 111 | 112 | org.springframework.boot 113 | spring-boot-starter 114 | ${spring-boot.version} 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | org.sonatype.central 125 | central-publishing-maven-plugin 126 | 0.4.0 127 | true 128 | 129 | poldroc 130 | true 131 | 132 | 133 | 134 | 135 | org.apache.maven.plugins 136 | maven-source-plugin 137 | 2.2.1 138 | 139 | 140 | attach-sources 141 | 142 | jar-no-fork 143 | 144 | 145 | 146 | 147 | 148 | 149 | org.apache.maven.plugins 150 | maven-javadoc-plugin 151 | 2.9.1 152 | 153 | 154 | attach-javadocs 155 | 156 | jar 157 | 158 | 159 | 160 | 161 | 162 | org.apache.maven.plugins 163 | maven-gpg-plugin 164 | 1.5 165 | 166 | 167 | F:\Environment\GnuPG\bin\gpg.exe 168 | 169 | 170 | 171 | sign-artifacts 172 | verify 173 | 174 | sign 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | Poldroc 186 | engroc@foxmail.com 187 | 188 | 189 | 190 | 191 | 192 | Apache License, Version 2.0 193 | http://www.apache.org/licenses/LICENSE-2.0.txt 194 | 195 | 196 | 197 | 198 | scm:git:git://github.com/Poldroc/roc-retry.git 199 | scm:git:ssh://github.com/Poldroc/roc-retry.git 200 | https://github.com/Poldroc/roc-retry/tree/master 201 | 202 | 203 | https://github.com/Poldroc/roc-retry.git 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /retry-annotation/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.poldroc 8 | roc-retry 9 | 1.1 10 | 11 | 12 | retry-annotation 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | cglib 23 | cglib 24 | 25 | 26 | ${project.groupId} 27 | retry-api 28 | 29 | 30 | ${project.groupId} 31 | retry-common 32 | 33 | 34 | ${project.groupId} 35 | retry-core 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/annotation/Retry.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.annotation; 2 | 3 | import com.poldroc.retry.annotation.annotation.metadata.RetryAble; 4 | import com.poldroc.retry.annotation.handler.impl.DefaultRetryAbleHandler; 5 | import com.poldroc.retry.api.support.condition.RetryCondition; 6 | import com.poldroc.retry.api.support.listen.RetryListen; 7 | import com.poldroc.retry.api.support.recover.Recover; 8 | import com.poldroc.retry.core.core.retry.DefaultRetry; 9 | import com.poldroc.retry.core.support.condition.ExceptionCauseRetryCondition; 10 | import com.poldroc.retry.core.support.listen.NoRetryListen; 11 | import com.poldroc.retry.core.support.recover.NoRecover; 12 | 13 | import java.lang.annotation.*; 14 | 15 | /** 16 | * 重试注解 17 | * 1. 实际需要,只允许放在方法上。 18 | * 2. 保持注解和接口的一致性 {@link com.poldroc.retry.api.core.Retry} 接口 19 | * 20 | * @author Poldroc 21 | * @since 2024/7/17 22 | */ 23 | 24 | @Documented 25 | @Inherited 26 | @Target(ElementType.METHOD) 27 | @Retention(RetentionPolicy.RUNTIME) 28 | @RetryAble(DefaultRetryAbleHandler.class) 29 | public @interface Retry { 30 | 31 | /** 32 | * 重试类实现 33 | * 34 | * @return 重试 35 | */ 36 | Class retry() default DefaultRetry.class; 37 | 38 | /** 39 | * 最大尝试次数 40 | * 1. 默认为3 包含方法第一次正常执行的次数 41 | * 42 | * @return 次数 43 | */ 44 | int maxAttempt() default 3; 45 | 46 | /** 47 | * 重试触发的场景 48 | * 1. 默认为异常触发 49 | * 50 | * @return 重试触发的场景 51 | */ 52 | Class condition() default ExceptionCauseRetryCondition.class; 53 | 54 | /** 55 | * 监听器 56 | * 1. 默认不进行监听 57 | * 58 | * @return 监听器 59 | */ 60 | Class listen() default NoRetryListen.class; 61 | 62 | /** 63 | * 恢复操作 64 | * 1. 默认不进行任何恢复操作 65 | * 66 | * @return 恢复操作对应的类 67 | */ 68 | Class recover() default NoRecover.class; 69 | 70 | /** 71 | * 重试等待策略 72 | * 1. 支持指定多个,如果不指定,则不进行任何等待, 73 | * 74 | * @return 重试等待策略 75 | */ 76 | RetryWait[] waits() default {}; 77 | } 78 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/annotation/RetryWait.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.annotation; 2 | 3 | import com.poldroc.retry.annotation.annotation.metadata.RetryWaitAble; 4 | import com.poldroc.retry.annotation.handler.impl.DefaultRetryWaitAbleHandler; 5 | import com.poldroc.retry.core.constant.RetryWaitConst; 6 | import com.poldroc.retry.core.support.wait.NoRetryWait; 7 | 8 | import java.lang.annotation.*; 9 | /** 10 | * 重试等待策略 11 | * 1. 为了对应重试策略,所有的内置注解应该实现当前的注解。 12 | * 2. 是否允许自定义注解? 13 | *

14 | * 当注解+对象同时出现的时候,视为组合。 15 | * 16 | * @author Poldroc 17 | * @since 2024/7/17 18 | */ 19 | 20 | @Retention(RetentionPolicy.RUNTIME) 21 | @Inherited 22 | @Documented 23 | @Target(ElementType.ANNOTATION_TYPE) 24 | @RetryWaitAble(DefaultRetryWaitAbleHandler.class) 25 | public @interface RetryWait { 26 | 27 | /** 28 | * 默认值 29 | * 1. fixed 模式,则对应固定等待时间 30 | * 2. 递增 31 | * @return 默认值 32 | */ 33 | long value() default RetryWaitConst.DEFAULT_VALUE_MILLS; 34 | 35 | /** 36 | * 最小值 37 | * @return 最小值 38 | */ 39 | long min() default RetryWaitConst.DEFAULT_MIN_MILLS; 40 | 41 | /** 42 | * 最大值 43 | * @return 最大值 44 | */ 45 | long max() default RetryWaitConst.DEFAULT_MAX_MILLS; 46 | 47 | /** 48 | * 影响因数 49 | * 1. 递增重试,默认为 {@link RetryWaitConst#INCREASE_MILLS_FACTOR} 50 | * 2. 指数模式。默认为 {@link RetryWaitConst#MULTIPLY_FACTOR} 51 | * @return 影响因数 52 | */ 53 | double factor() default Double.MIN_VALUE; 54 | 55 | /** 56 | * 指定重试的等待时间 class 信息 57 | * @return 重试等待时间 class 58 | */ 59 | Class retryWait() default NoRetryWait.class; 60 | 61 | } 62 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/annotation/metadata/RetryAble.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.annotation.metadata; 2 | 3 | import com.poldroc.retry.annotation.handler.RetryAbleHandler; 4 | 5 | import java.lang.annotation.*; 6 | /** 7 | * 可重试注解 8 | * 1.用于注解之上用于指定重试信息 9 | * @see com.poldroc.retry.api.context.RetryContext 10 | * @author Poldroc 11 | * @since 2024/7/13 12 | */ 13 | 14 | @Documented 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Target(ElementType.ANNOTATION_TYPE) 17 | @Inherited 18 | public @interface RetryAble { 19 | 20 | /** 21 | * 对应的注解处理器 22 | * @return class 信息 23 | */ 24 | Class value(); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/annotation/metadata/RetryWaitAble.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.annotation.metadata; 2 | 3 | import com.poldroc.retry.annotation.handler.RetryWaitAbleHandler; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * 可重试等待的注解 9 | * 1. 用于注解之上指定等待注解的处理信息 10 | * @see com.poldroc.retry.api.context.RetryWaitContext 11 | * @author Poldroc 12 | * @since 2024/7/15 13 | */ 14 | 15 | @Documented 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target(ElementType.ANNOTATION_TYPE) 18 | @Inherited 19 | public @interface RetryWaitAble { 20 | 21 | /** 22 | * 对应的注解处理器 23 | * @return class 信息 24 | */ 25 | Class value(); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/core/RetryTemplate.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.core; 2 | 3 | import com.poldroc.retry.annotation.proxy.cglib.CglibProxy; 4 | import com.poldroc.retry.annotation.proxy.dynamic.DynamicProxy; 5 | import com.poldroc.retry.annotation.proxy.none.NoneProxy; 6 | import com.poldroc.retry.common.constant.enums.ProxyTypeEnum; 7 | import com.poldroc.retry.common.support.proxy.ProxyFactory; 8 | 9 | /** 10 | * 重试模板 11 | * 12 | * @author Poldroc 13 | * @since 2024/7/18 14 | */ 15 | 16 | public class RetryTemplate { 17 | 18 | private RetryTemplate() { 19 | } 20 | 21 | public static R getProxyObject(R object) { 22 | ProxyTypeEnum proxyType = ProxyFactory.getProxyType(object); 23 | if (ProxyTypeEnum.NONE.equals(proxyType)) { 24 | return (R) new NoneProxy(object).proxy(); 25 | } else if (ProxyTypeEnum.DYNAMIC.equals(proxyType)) { 26 | return (R) new DynamicProxy(object).proxy(); 27 | } else { 28 | return (R) new CglibProxy(object).proxy(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/handler/RetryAbleHandler.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.handler; 2 | 3 | import com.poldroc.retry.api.context.RetryContext; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.util.concurrent.Callable; 7 | /** 8 | * 可重试注解处理器 9 | * @author Poldroc 10 | * @since 2024/7/13 11 | */ 12 | 13 | public interface RetryAbleHandler { 14 | 15 | /** 16 | * 根据注解信息构建上下文 17 | * @param annotation 可重试注解 18 | * @param callable 待重试方法 19 | * @return 重试上下文 20 | */ 21 | RetryContext build(final A annotation, 22 | final Callable callable); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/handler/RetryWaitAbleHandler.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.handler; 2 | 3 | import com.poldroc.retry.api.context.RetryWaitContext; 4 | 5 | import java.lang.annotation.Annotation; 6 | /** 7 | * 重试等待处理器 8 | * @author Poldroc 9 | * @since 2024/7/15 10 | */ 11 | 12 | public interface RetryWaitAbleHandler { 13 | 14 | /** 15 | * 根据注解信息构建上下文 16 | * @param annotation 可重试等待注解 17 | * @return 重试等待上下文 18 | */ 19 | RetryWaitContext build(final A annotation); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/handler/impl/DefaultRetryAbleHandler.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.handler.impl; 2 | 3 | import com.poldroc.retry.annotation.annotation.Retry; 4 | import com.poldroc.retry.annotation.annotation.RetryWait; 5 | import com.poldroc.retry.annotation.handler.RetryAbleHandler; 6 | import com.poldroc.retry.api.context.RetryContext; 7 | import com.poldroc.retry.api.context.RetryWaitContext; 8 | import com.poldroc.retry.common.annotation.ThreadSafe; 9 | import com.poldroc.retry.common.support.instance.Instance; 10 | import com.poldroc.retry.common.support.instance.impl.InstanceFactory; 11 | import com.poldroc.retry.core.core.RetryWaiter; 12 | import com.poldroc.retry.core.core.Retryer; 13 | import com.poldroc.retry.core.support.wait.NoRetryWait; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Collections; 17 | import java.util.List; 18 | import java.util.concurrent.Callable; 19 | /** 20 | * 默认的重试处理器 21 | * @author Poldroc 22 | * @since 2024/7/18 23 | */ 24 | 25 | @ThreadSafe 26 | public class DefaultRetryAbleHandler implements RetryAbleHandler { 27 | 28 | @Override 29 | public RetryContext build(Retry annotation, Callable callable) { 30 | Instance instance = InstanceFactory.getInstance(); 31 | return Retryer.newInstance() 32 | .callable(callable) 33 | .retry(instance.threadSafe(annotation.retry())) 34 | .condition(instance.threadSafe(annotation.condition())) 35 | .maxAttempt(annotation.maxAttempt()) 36 | .recover(instance.threadSafe(annotation.recover())) 37 | .listen(instance.threadSafe(annotation.listen())) 38 | .retryWaitContext(buildRetryWaitContext(annotation)) 39 | .context(); 40 | } 41 | 42 | /** 43 | * 构建重试等待上下文 44 | * @param retry 重试信息 45 | * @return 上下文列表 46 | */ 47 | private List> buildRetryWaitContext(Retry retry) { 48 | if (retry == null) { 49 | return Collections.singletonList(RetryWaiter.retryWait(NoRetryWait.class).context()); 50 | } 51 | RetryWait[] waits = retry.waits(); 52 | if (waits == null || waits.length == 0) { 53 | return Collections.singletonList(RetryWaiter.retryWait(NoRetryWait.class).context()); 54 | } 55 | List> retryWaitContexts = new ArrayList<>(); 56 | DefaultRetryWaitAbleHandler defaultRetryWaitAbleHandler = InstanceFactory.getInstance().threadSafe(DefaultRetryWaitAbleHandler.class); 57 | for (RetryWait wait : waits) { 58 | retryWaitContexts.add(defaultRetryWaitAbleHandler.build(wait)); 59 | } 60 | return retryWaitContexts; 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/handler/impl/DefaultRetryWaitAbleHandler.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.handler.impl; 2 | 3 | import com.poldroc.retry.annotation.annotation.RetryWait; 4 | import com.poldroc.retry.annotation.handler.RetryWaitAbleHandler; 5 | import com.poldroc.retry.api.context.RetryWaitContext; 6 | import com.poldroc.retry.common.annotation.ThreadSafe; 7 | import com.poldroc.retry.core.core.RetryWaiter; 8 | /** 9 | * 默认的重试等待处理器 10 | * @author Poldroc 11 | * @since 2024/7/17 12 | */ 13 | @ThreadSafe 14 | public class DefaultRetryWaitAbleHandler implements RetryWaitAbleHandler { 15 | @Override 16 | public RetryWaitContext build(RetryWait annotation) { 17 | return RetryWaiter 18 | .retryWait(annotation.retryWait()) 19 | .min(annotation.min()) 20 | .max(annotation.max()) 21 | .factor(annotation.factor()) 22 | .value(annotation.value()) 23 | .context(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/handler/method/RetryMethodHandler.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.handler.method; 2 | 3 | import com.poldroc.retry.annotation.annotation.metadata.RetryAble; 4 | import com.poldroc.retry.annotation.handler.RetryAbleHandler; 5 | import com.poldroc.retry.annotation.model.RetryAbleBean; 6 | import com.poldroc.retry.annotation.proxy.IMethodHandler; 7 | import com.poldroc.retry.api.context.RetryContext; 8 | import com.poldroc.retry.common.annotation.ThreadSafe; 9 | import com.poldroc.retry.common.support.instance.impl.InstanceFactory; 10 | import com.poldroc.retry.core.core.Retryer; 11 | 12 | import java.lang.annotation.Annotation; 13 | import java.lang.reflect.Method; 14 | import java.util.Arrays; 15 | import java.util.Optional; 16 | import java.util.concurrent.Callable; 17 | 18 | /** 19 | * 默认的重试方法实现 20 | * 21 | * @author Poldroc 22 | * @since 2024/7/14 23 | */ 24 | @ThreadSafe 25 | public class RetryMethodHandler implements IMethodHandler { 26 | @Override 27 | public Object handle(Object obj, Method method, Object[] args) throws Throwable { 28 | // 1. 判断注解信息 29 | Optional retryAnnotationOpt = findRetryAnnotation(method, args); 30 | // 没有重试注解 31 | if (!retryAnnotationOpt.isPresent()) { 32 | return method.invoke(obj, args); 33 | } 34 | // 2. 包含注解才进行处理 35 | RetryAbleBean retryAbleBean = retryAnnotationOpt.get(); 36 | Callable callable = buildCallable(obj, method, args); 37 | RetryAbleHandler retryAbleHandler = InstanceFactory.getInstance().threadSafe(retryAbleBean.retryAble().value()); 38 | 39 | // 3. 根据注解的内容构建执行上下文 40 | RetryContext retryContext = retryAbleHandler.build(retryAbleBean.annotation(), callable); 41 | retryContext.params(args); 42 | return Retryer.newInstance().retryCall(retryContext); 43 | } 44 | 45 | 46 | /** 47 | * 重试调用 48 | * @param retryAbleBean 重试调用对象 49 | * @param callable 待重试方法 50 | * @return 执行结果 51 | */ 52 | public Object retryCall(RetryAbleBean retryAbleBean,Callable callable) { 53 | // 获得注解处理器 54 | RetryAbleHandler retryAbleHandler = InstanceFactory.getInstance().threadSafe(retryAbleBean.retryAble().value()); 55 | RetryContext retryContext = retryAbleHandler.build(retryAbleBean.annotation(), callable); 56 | retryContext.params(retryAbleBean.args()); 57 | return Retryer.newInstance().retryCall(retryContext); 58 | } 59 | 60 | /** 61 | * 查找重试注解 62 | * 63 | * @param method 方法 64 | * @param args 参数 65 | * @return 重试注解 66 | */ 67 | public Optional findRetryAnnotation(Method method, 68 | Object[] args) { 69 | Annotation[] annotations = method.getAnnotations(); 70 | if (annotations == null || annotations.length == 0) { 71 | return Optional.empty(); 72 | } 73 | for (Annotation annotation : annotations) { 74 | RetryAble retryAble = annotation.annotationType().getAnnotation(RetryAble.class); 75 | if (retryAble != null) { 76 | RetryAbleBean bean = new RetryAbleBean(); 77 | bean.retryAble(retryAble) 78 | .annotation(annotation) 79 | .args(args); 80 | return Optional.of(bean); 81 | } 82 | } 83 | return Optional.empty(); 84 | } 85 | 86 | /** 87 | * 构建 callable 88 | * 89 | * @param proxy 代理对象 90 | * @param method 方法 91 | * @param args 参数 92 | * @return callable 93 | */ 94 | private Callable buildCallable(final Object proxy, final Method method, final Object[] args) { 95 | return () -> method.invoke(proxy, args); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/model/RetryAbleBean.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.model; 2 | 3 | import com.poldroc.retry.annotation.annotation.metadata.RetryAble; 4 | 5 | import java.lang.annotation.Annotation; 6 | 7 | /** 8 | * 可重试注解对象 9 | * 10 | * @author Poldroc 11 | * @since 2024/7/13 12 | */ 13 | public class RetryAbleBean { 14 | 15 | /** 16 | * 注解处理器信息 17 | */ 18 | private RetryAble retryAble; 19 | 20 | /** 21 | * 原始注解信息 22 | * @see com.poldroc.retry.annotation.annotation.Retry 23 | */ 24 | private Annotation annotation; 25 | 26 | /** 27 | * 请求参数 28 | */ 29 | private Object[] args; 30 | 31 | public RetryAble retryAble() { 32 | return retryAble; 33 | } 34 | 35 | public RetryAbleBean retryAble(RetryAble retryAble) { 36 | this.retryAble = retryAble; 37 | return this; 38 | } 39 | 40 | public Annotation annotation() { 41 | return annotation; 42 | } 43 | 44 | public RetryAbleBean annotation(Annotation annotation) { 45 | this.annotation = annotation; 46 | return this; 47 | } 48 | 49 | public Object[] args() { 50 | return args; 51 | } 52 | 53 | public RetryAbleBean args(Object[] args) { 54 | this.args = args; 55 | return this; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/proxy/IMethodHandler.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.proxy; 2 | 3 | import java.lang.reflect.Method; 4 | public interface IMethodHandler { 5 | Object handle(Object var1, Method var2, Object[] var3) throws Throwable; 6 | } 7 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/proxy/IProxy.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.proxy; 2 | 3 | public interface IProxy { 4 | Object proxy(); 5 | } 6 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/proxy/cglib/CglibProxy.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.proxy.cglib; 2 | 3 | import com.poldroc.retry.annotation.handler.method.RetryMethodHandler; 4 | import com.poldroc.retry.annotation.proxy.IProxy; 5 | import com.poldroc.retry.common.annotation.ThreadSafe; 6 | import com.poldroc.retry.common.support.instance.impl.InstanceFactory; 7 | import net.sf.cglib.proxy.Enhancer; 8 | import net.sf.cglib.proxy.MethodInterceptor; 9 | import net.sf.cglib.proxy.MethodProxy; 10 | 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.lang.reflect.Method; 13 | /** 14 | * CGLIB 代理 15 | * @author Poldroc 16 | * @since 2024/7/14 17 | */ 18 | 19 | @ThreadSafe 20 | public class CglibProxy implements IProxy, MethodInterceptor { 21 | /** 22 | * 被代理的对象 23 | */ 24 | private final Object target; 25 | 26 | public CglibProxy(Object target) { 27 | this.target = target; 28 | } 29 | 30 | @Override 31 | public Object proxy() { 32 | Enhancer enhancer = new Enhancer(); 33 | // 目标对象类 34 | enhancer.setSuperclass(target.getClass()); 35 | // 设置回调 36 | enhancer.setCallback(this); 37 | // 通过字节码技术创建目标对象类的子类实例作为代理 38 | return enhancer.create(); 39 | } 40 | 41 | @Override 42 | public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 43 | try { 44 | RetryMethodHandler retryMethodHandler = InstanceFactory.getInstance().threadSafe(RetryMethodHandler.class); 45 | return retryMethodHandler.handle(target, method, objects); 46 | } catch (InvocationTargetException ex) { 47 | // 程序内部没有处理的异常 48 | throw ex.getTargetException(); 49 | } catch (Throwable throwable) { 50 | throw throwable; 51 | } 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/proxy/dynamic/DynamicProxy.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.proxy.dynamic; 2 | 3 | import com.poldroc.retry.annotation.handler.method.RetryMethodHandler; 4 | import com.poldroc.retry.annotation.proxy.IProxy; 5 | import com.poldroc.retry.common.annotation.ThreadSafe; 6 | import com.poldroc.retry.common.support.instance.impl.InstanceFactory; 7 | 8 | import java.lang.reflect.InvocationHandler; 9 | import java.lang.reflect.InvocationTargetException; 10 | import java.lang.reflect.Method; 11 | import java.lang.reflect.Proxy; 12 | 13 | /** 14 | * 动态代理 15 | * 16 | * @author Poldroc 17 | * @since 2024/7/14 18 | */ 19 | 20 | @ThreadSafe 21 | public class DynamicProxy implements InvocationHandler, IProxy { 22 | 23 | /** 24 | * 目标对象 25 | */ 26 | private final Object target; 27 | 28 | public DynamicProxy(Object target) { 29 | this.target = target; 30 | } 31 | 32 | @Override 33 | public Object proxy() { 34 | InvocationHandler handler = new DynamicProxy(target); 35 | return Proxy.newProxyInstance(handler.getClass().getClassLoader(), 36 | target.getClass().getInterfaces(), handler); 37 | } 38 | 39 | @Override 40 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 41 | try { 42 | RetryMethodHandler retryMethodHandler = InstanceFactory.getInstance().threadSafe(RetryMethodHandler.class); 43 | return retryMethodHandler.handle(target, method, args); 44 | } catch (InvocationTargetException ex) { 45 | // 程序内部没有处理的异常 46 | throw ex.getTargetException(); 47 | } catch (Throwable throwable) { 48 | throw throwable; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /retry-annotation/src/main/java/com/poldroc/retry/annotation/proxy/none/NoneProxy.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.annotation.proxy.none; 2 | 3 | import com.poldroc.retry.annotation.proxy.IProxy; 4 | import com.poldroc.retry.common.annotation.ThreadSafe; 5 | 6 | import java.lang.reflect.InvocationHandler; 7 | import java.lang.reflect.Method; 8 | 9 | @ThreadSafe 10 | public class NoneProxy implements InvocationHandler, IProxy { 11 | private final Object target; 12 | 13 | public NoneProxy(Object target) { 14 | this.target = target; 15 | } 16 | 17 | public Object proxy() { 18 | return this.target; 19 | } 20 | 21 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 22 | return method.invoke(proxy, args); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /retry-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.poldroc 8 | roc-retry 9 | 1.1 10 | 11 | 12 | retry-api 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/context/RetryContext.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.context; 2 | 3 | import com.poldroc.retry.api.core.Retry; 4 | import com.poldroc.retry.api.support.block.RetryBlock; 5 | import com.poldroc.retry.api.support.condition.RetryCondition; 6 | import com.poldroc.retry.api.support.listen.RetryListen; 7 | import com.poldroc.retry.api.support.recover.Recover; 8 | import com.poldroc.retry.api.support.stop.RetryStop; 9 | 10 | import java.util.List; 11 | import java.util.concurrent.Callable; 12 | 13 | /** 14 | * 重试执行上下文 15 | * 限定实现类的参数 16 | * @author Poldroc 17 | * @since 2024/7/11 18 | */ 19 | 20 | public interface RetryContext { 21 | 22 | /** 23 | * 重试实现类 24 | * @return 重试 25 | */ 26 | Retry retry(); 27 | 28 | /** 29 | * 重试生效条件 30 | * @return 生效条件 31 | */ 32 | RetryCondition condition(); 33 | 34 | /** 35 | * 重试等待上下文 36 | * @return 重试等待上下文 37 | */ 38 | List> waitContext(); 39 | 40 | /** 41 | * 阻塞方式 42 | * @return 阻塞方式 43 | */ 44 | RetryBlock block(); 45 | 46 | /** 47 | * 停止方式 48 | * @return 停止方式 49 | */ 50 | RetryStop stop(); 51 | 52 | /** 53 | * 可执行的方法 54 | * @return 方法 55 | */ 56 | Callable callable(); 57 | 58 | /** 59 | * 监听信息列表 60 | * @return 监听信息列表 61 | */ 62 | RetryListen listen(); 63 | 64 | /** 65 | * 恢复方式 66 | * @return 恢复方式 67 | */ 68 | Recover recover(); 69 | 70 | /** 71 | * 请求参数 72 | * @return 请求参数 73 | */ 74 | Object[] params(); 75 | 76 | /** 77 | * 设置上下文的信息 78 | * @param params 参数 79 | * @return this 80 | */ 81 | RetryContext params(Object[] params); 82 | 83 | 84 | } 85 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/context/RetryWaitContext.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.context; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.wait.RetryWait; 5 | /** 6 | * 重试等待策略上下文 7 | * @author Poldroc 8 | * @since 2024/7/11 9 | */ 10 | 11 | public interface RetryWaitContext extends RetryAttempt { 12 | /** 13 | * 基础值(毫秒) 14 | * 1. fixed: 固定间隔 15 | * 2. 递增/指数:为初始值 16 | * 3. random/noRetry 这个值会被忽略 17 | * @return 基础值 18 | */ 19 | long value(); 20 | 21 | /** 22 | * 最小等待时间(毫秒) 23 | * @return 最小等待时间(毫秒) 24 | */ 25 | long min(); 26 | 27 | /** 28 | * 最大等待时间(毫秒) 29 | * @return 最大等待时间(毫秒) 30 | */ 31 | long max(); 32 | 33 | /** 34 | * 变换因子(递增/毫秒) 35 | * 1. 递增:每次增加的时间 36 | * 2. 指数:每次乘的因数 37 | * @return 变换因子 38 | */ 39 | double factor(); 40 | 41 | /** 42 | * 对应的 class 信息 43 | * @return class 信息 44 | */ 45 | Class retryWait(); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/core/Retry.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.core; 2 | 3 | import com.poldroc.retry.api.context.RetryContext; 4 | /** 5 | * 重试接口 6 | * @author Poldroc 7 | * @since 2024/7/11 8 | */ 9 | 10 | public interface Retry { 11 | 12 | 13 | /** 14 | * 执行重试 15 | * @param context 执行上下文 16 | * @return 执行结果 17 | */ 18 | R retryCall(final RetryContext context); 19 | } 20 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/exception/RetryException.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.exception; 2 | 3 | /** 4 | * 重试异常 5 | * 6 | * @author Poldroc 7 | * @since 2024/7/11 8 | */ 9 | 10 | public class RetryException extends RuntimeException { 11 | 12 | public RetryException() { 13 | } 14 | 15 | public RetryException(String message) { 16 | super(message); 17 | } 18 | 19 | public RetryException(String message, Throwable cause) { 20 | super(message, cause); 21 | } 22 | 23 | public RetryException(Throwable cause) { 24 | super(cause); 25 | } 26 | 27 | public RetryException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 28 | super(message, cause, enableSuppression, writableStackTrace); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/model/AttemptTime.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.model; 2 | 3 | import java.util.Date; 4 | /** 5 | * 尝试执行时间接口 6 | * @author Poldroc 7 | * @since 2024/7/11 8 | */ 9 | 10 | public interface AttemptTime { 11 | 12 | /** 13 | * 开始时间 14 | * @return 开始时间 15 | */ 16 | Date startTime(); 17 | 18 | /** 19 | * 结束时间 20 | * @return 结束时间 21 | */ 22 | Date endTime(); 23 | 24 | /** 25 | * 消耗的时间(毫秒) 26 | * @return 消耗的时间 27 | */ 28 | long costTimeInMills(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/model/RetryAttempt.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 重试信息接口 7 | * @author Poldroc 8 | * @since 2024/7/11 9 | */ 10 | 11 | public interface RetryAttempt { 12 | 13 | /** 14 | * 获取方法执行的结果 15 | * @return 方法执行的结果 16 | */ 17 | R result(); 18 | 19 | /** 20 | * 获取重试次数 21 | * @return 重试次数 22 | */ 23 | int attempt(); 24 | 25 | /** 26 | * 获取异常信息 27 | * @return 异常信息 28 | */ 29 | Throwable cause(); 30 | 31 | /** 32 | * 获取消耗时间 33 | * @return 消耗时间 34 | */ 35 | AttemptTime time(); 36 | 37 | /** 38 | * 获取重试历史信息 39 | * @return 重试历史信息 40 | */ 41 | List> history(); 42 | 43 | /** 44 | * 获取请求参数 45 | * @return 请求参数 46 | */ 47 | Object[] params(); 48 | } 49 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/model/WaitTime.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.model; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | /** 5 | * 等待时间接口 6 | * @author Poldroc 7 | * @since 2024/7/11 8 | */ 9 | 10 | public interface WaitTime { 11 | 12 | /** 13 | * 等待时间 14 | * @return 时间 15 | */ 16 | long time(); 17 | 18 | /** 19 | * 等待时间单位 20 | * @return 时间单位 21 | */ 22 | TimeUnit unit(); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/support/block/RetryBlock.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.support.block; 2 | 3 | import com.poldroc.retry.api.model.WaitTime; 4 | /** 5 | * 阻塞的方式 6 | * @author Poldroc 7 | * @since 2024/7/11 8 | */ 9 | 10 | public interface RetryBlock { 11 | 12 | /** 13 | * 重试等待阻塞方式 14 | * @param waitTime 等待时间 15 | */ 16 | void block(final WaitTime waitTime); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/support/condition/RetryCondition.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.support.condition; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | 5 | /** 6 | * 重试执行的条件 7 | * @author Poldroc 8 | * @since 2024/7/11 9 | */ 10 | public interface RetryCondition { 11 | 12 | /** 13 | * 判断是否满足重试条件 14 | * @param retryAttempt 重试相关信息 15 | * @return 是否满足条件 16 | */ 17 | boolean condition(final RetryAttempt retryAttempt); 18 | } 19 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/support/listen/RetryListen.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.support.listen; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | /** 5 | * 重试监听接口 6 | * @author Poldroc 7 | * @since 2024/7/11 8 | */ 9 | 10 | public interface RetryListen { 11 | 12 | /** 13 | * 执行重试监听,每次重试执行的最后触发监听器 14 | * @param attempt 重试 15 | * @param 泛型 16 | */ 17 | void listen(final RetryAttempt attempt); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/support/recover/Recover.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.support.recover; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | 5 | /** 6 | * 恢复现场接口 7 | * 1. 只会在所有的尝试都执行完成之后才会执行。 8 | * 2. 触发了重试,且所有的重试都完成了,但结果依然是失败。 9 | * 3. 根据实际使用,一次失败对应的 recover 应该是唯一的。 10 | * 注意:实现类应该有无参构造函数 11 | * @author Poldroc 12 | * @since 2024/7/11 13 | * 14 | */ 15 | public interface Recover { 16 | 17 | /** 18 | * 执行恢复 19 | * @param retryAttempt 重试信息 20 | * @param 泛型 21 | */ 22 | void recover(final RetryAttempt retryAttempt); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/support/stop/RetryStop.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.support.stop; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | /** 5 | * 结束的条件 6 | * @author Poldroc 7 | * @since 2024/7/11 8 | */ 9 | 10 | public interface RetryStop { 11 | 12 | /** 13 | * 停止执行重试 14 | * @param attempt 执行信息 15 | * @return 是否停止 16 | */ 17 | boolean stop(final RetryAttempt attempt); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /retry-api/src/main/java/com/poldroc/retry/api/support/wait/RetryWait.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.api.support.wait; 2 | 3 | import com.poldroc.retry.api.context.RetryWaitContext; 4 | import com.poldroc.retry.api.model.WaitTime; 5 | 6 | import java.lang.annotation.Annotation; 7 | 8 | /** 9 | * 重试等待策略 10 | * 1. 所有的实现必须要有无参构造器,因为会基于反射处理类信息(newInstance)。 11 | * 2. 尽可能的保证为线程安全的,比如 stateless。 12 | * @author Poldroc 13 | * @since 2024/7/11 14 | */ 15 | 16 | public interface RetryWait{ 17 | 18 | /** 19 | * 计算等待时间 20 | * @param retryWaitContext 上下文信息 21 | * @return 等待时间的结果信息 22 | */ 23 | WaitTime waitTime(final RetryWaitContext retryWaitContext); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /retry-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.poldroc 8 | roc-retry 9 | 1.1 10 | 11 | 12 | retry-common 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /retry-common/src/main/java/com/poldroc/retry/common/annotation/NotThreadSafe.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.common.annotation; 2 | 3 | import java.lang.annotation.*; 4 | /** 5 | * 线程不安全安全注解 6 | * @author Poldroc 7 | * @since 2024/7/11 8 | */ 9 | 10 | @Documented 11 | @Inherited 12 | @Target({ElementType.TYPE, ElementType.METHOD}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface NotThreadSafe { 15 | } 16 | -------------------------------------------------------------------------------- /retry-common/src/main/java/com/poldroc/retry/common/annotation/ThreadSafe.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.common.annotation; 2 | 3 | import java.lang.annotation.*; 4 | /** 5 | * 线程安全注解 6 | * 放在类上,标识当前类为线程安全的。 7 | * 放在方法上,标识方法是线程安全的。 8 | *

9 | * 注意:目前此注解仅供内部使用,用来标识类是否线程安全。(表示作者的预期) 真正效果需要验证。 10 | *

11 | * 后期用途:可能会直接基于 class 进行反射创建,要求有些类需要显示指定这个注解。 12 | * @author Poldroc 13 | * @since 2024/7/11 14 | */ 15 | 16 | @Documented 17 | @Inherited 18 | @Target({ElementType.TYPE, ElementType.METHOD}) 19 | @Retention(RetentionPolicy.RUNTIME) 20 | public @interface ThreadSafe { 21 | } 22 | -------------------------------------------------------------------------------- /retry-common/src/main/java/com/poldroc/retry/common/constant/enums/ProxyTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.common.constant.enums; 2 | 3 | public enum ProxyTypeEnum { 4 | NONE, 5 | DYNAMIC, 6 | CGLIB; 7 | 8 | private ProxyTypeEnum() { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /retry-common/src/main/java/com/poldroc/retry/common/support/instance/Instance.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.common.support.instance; 2 | 3 | /** 4 | * 实例化对象的接口 5 | * 1. 使用此类的 class 必须有无参构造器 6 | * 2. 当前类出于测试阶段。 7 | * @author Poldroc 8 | * @since 2024/7/11 9 | */ 10 | 11 | public interface Instance { 12 | 13 | /** 14 | * 获取对象的单例对象 15 | * 1. 需要保证对象的线程安全性。 16 | * 2. 只有在同一个分组返回的对象才会是单例,否则返回 newInstance() 17 | * @param tClass class 类型 18 | * @param groupName 分组名称 19 | * @param 泛型 20 | * @return 实例化对象 21 | */ 22 | T singleton(final Class tClass, 23 | final String groupName); 24 | 25 | /** 26 | * 获取对象的单例对象 27 | * 1. 需要保证对象的线程安全性。 28 | * @param tClass class 类型 29 | * @param 泛型 30 | * @return 实例化对象 31 | */ 32 | T singleton(final Class tClass); 33 | 34 | /** 35 | * 获取每个线程内唯一的实例化对象 36 | * 注意:可能会内存泄漏的场景。 37 | * (1) 只要这个线程对象被gc回收,就不会出现内存泄露,但在threadLocal设为null和线程结束这段时间不会被回收的,就发生了我们认为的内存泄露。 38 | * 最要命的是线程对象不被回收的情况,这就发生了真正意义上的内存泄露。比如使用线程池的时候,线程结束是不会销毁的,会再次使用的。就可能出现内存泄露。  39 | * 参考资料:https://www.cnblogs.com/onlywujun/p/3524675.html 40 | * @param tClass class 类型 41 | * @param 泛型 42 | * @return 实例化对象 43 | * @see java.lang.ref.WeakReference 弱引用 44 | */ 45 | T threadLocal(final Class tClass); 46 | 47 | /** 48 | * 多例对象,每次都是全新的创建 49 | * @param tClass class 类型 50 | * @param 泛型 51 | * @return 实例化对象 52 | */ 53 | T multiple(final Class tClass); 54 | 55 | /** 56 | * 线程安全对象 57 | * 1. 判断当前类是否拥有 {@link com.poldroc.retry.common.annotation.ThreadSafe} 注解, 58 | * 如果有,则直接创建单例对象。如果不是,则创建多例对象。 59 | * @param tClass class 类型 60 | * @param 泛型 61 | * @return 实例化对象 62 | */ 63 | T threadSafe(final Class tClass); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /retry-common/src/main/java/com/poldroc/retry/common/support/instance/impl/InstanceFactory.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.common.support.instance.impl; 2 | 3 | import com.poldroc.retry.common.annotation.ThreadSafe; 4 | import com.poldroc.retry.common.support.instance.Instance; 5 | import com.poldroc.retry.common.util.ArgUtil; 6 | 7 | import java.util.Map; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | /** 11 | * 实例化工厂类 12 | * 13 | * @author Poldroc 14 | * @since 2024/7/11 15 | */ 16 | @ThreadSafe 17 | public class InstanceFactory implements Instance { 18 | 19 | private InstanceFactory() { 20 | } 21 | 22 | /** 23 | * 单例 map 对象 24 | * key 是 class 的全称 25 | */ 26 | private final Map singletonMap = new ConcurrentHashMap<>(); 27 | 28 | /** 29 | * 线程内的 map 对象 30 | */ 31 | private ThreadLocal> mapThreadLocal = new ThreadLocal<>(); 32 | 33 | /** 34 | * 静态内部类实现单例 35 | */ 36 | private static class SingletonHolder { 37 | private static final InstanceFactory INSTANCE_FACTORY = new InstanceFactory(); 38 | } 39 | 40 | /** 41 | * 获取单例对象 42 | * 43 | * @return 实例化对象 44 | */ 45 | public static InstanceFactory getInstance() { 46 | return SingletonHolder.INSTANCE_FACTORY; 47 | } 48 | 49 | @Override 50 | public T singleton(Class tClass, String groupName) { 51 | return getSingleton(tClass, groupName, singletonMap); 52 | } 53 | 54 | @Override 55 | public T singleton(Class tClass) { 56 | this.notNull(tClass); 57 | return this.getSingleton(tClass, singletonMap); 58 | } 59 | 60 | @Override 61 | public T threadLocal(Class tClass) { 62 | this.notNull(tClass); 63 | Map map = mapThreadLocal.get(); 64 | if (map == null) { 65 | map = new ConcurrentHashMap<>(); 66 | } 67 | T instance = this.getSingleton(tClass, map); 68 | mapThreadLocal.set(map); 69 | return instance; 70 | } 71 | 72 | @Override 73 | public T multiple(Class tClass) { 74 | this.notNull(tClass); 75 | try { 76 | return tClass.newInstance(); 77 | } catch (InstantiationException | IllegalAccessException e) { 78 | throw new RuntimeException("Create instance failed.", e); 79 | } 80 | } 81 | 82 | @Override 83 | public T threadSafe(Class tClass) { 84 | if (tClass.isAnnotationPresent(ThreadSafe.class)) { 85 | return this.singleton(tClass); 86 | } 87 | return this.multiple(tClass); 88 | } 89 | 90 | /** 91 | * 获取单例对象 92 | * 93 | * @param tClass class 类型 94 | * @param instanceMap 实例化对象 map 95 | * @return 单例对象 96 | */ 97 | @SuppressWarnings("unchecked") 98 | private T getSingleton(final Class tClass, final Map instanceMap) { 99 | this.notNull(tClass); 100 | 101 | final String fullClassName = tClass.getName(); 102 | T instance = (T) instanceMap.get(fullClassName); 103 | if (instance == null) { 104 | instance = this.multiple(tClass); 105 | instanceMap.put(fullClassName, instance); 106 | } 107 | return instance; 108 | } 109 | 110 | /** 111 | * 获取单例对象 112 | * 113 | * @param tClass 查询 tClass 114 | * @param group 分组信息 115 | * @param instanceMap 实例化对象 map 116 | * @return 单例对象 117 | */ 118 | @SuppressWarnings("unchecked") 119 | private T getSingleton(final Class tClass, 120 | final String group, final Map instanceMap) { 121 | this.notNull(tClass); 122 | ArgUtil.notEmpty(group, "key"); 123 | 124 | final String fullClassName = tClass.getName() + "-" + group; 125 | T instance = (T) instanceMap.get(fullClassName); 126 | if (instance == null) { 127 | instance = this.multiple(tClass); 128 | instanceMap.put(fullClassName, instance); 129 | } 130 | return instance; 131 | } 132 | 133 | /** 134 | * 断言参数不可为 null 135 | * 136 | * @param tClass class 信息 137 | */ 138 | private void notNull(final Class tClass) { 139 | ArgUtil.notNull(tClass, "class"); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /retry-common/src/main/java/com/poldroc/retry/common/support/proxy/ProxyFactory.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.common.support.proxy; 2 | 3 | import com.poldroc.retry.common.constant.enums.ProxyTypeEnum; 4 | 5 | import java.lang.reflect.Proxy; 6 | 7 | public class ProxyFactory { 8 | private ProxyFactory() { 9 | } 10 | 11 | public static ProxyTypeEnum getProxyType(Object object) { 12 | if (object == null) { 13 | return ProxyTypeEnum.NONE; 14 | } else { 15 | Class clazz = object.getClass(); 16 | return !clazz.isInterface() && !Proxy.isProxyClass(clazz) ? ProxyTypeEnum.CGLIB : ProxyTypeEnum.DYNAMIC; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /retry-common/src/main/java/com/poldroc/retry/common/util/ArgUtil.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.common.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 参数工具类 8 | * 9 | * @author Poldroc 10 | * @since 2024/7/11 11 | */ 12 | 13 | public final class ArgUtil { 14 | 15 | private ArgUtil() { 16 | } 17 | 18 | /** 19 | * 断言不为空 20 | * 21 | * @param object 对象 22 | * @param name 对象名称 23 | */ 24 | public static void notNull(Object object, String name) { 25 | if (null == object) { 26 | throw new IllegalArgumentException(name + " can not be null!"); 27 | } 28 | } 29 | 30 | /** 31 | * 校验字符串非空 32 | * 33 | * @param string 待检查的字符串 34 | * @param name 字符串的名称 35 | */ 36 | public static void notEmpty(String string, String name) { 37 | if (StringUtil.isEmpty(string)) { 38 | throw new IllegalArgumentException(name + " can not be null!"); 39 | } 40 | } 41 | 42 | public static void notEmpty(Object[] array, String name) { 43 | if (array == null || array.length == 0) { 44 | throw new IllegalArgumentException(name + " excepted is not empty!"); 45 | } else { 46 | Object[] var2 = array; 47 | int var3 = array.length; 48 | 49 | for (int var4 = 0; var4 < var3; ++var4) { 50 | Object object = var2[var4]; 51 | notNull(object, name + " element "); 52 | } 53 | } 54 | } 55 | 56 | public static void notEmpty(List list, String name) { 57 | if (list == null || list.isEmpty()) { 58 | throw new IllegalArgumentException(name + " excepted is not empty!"); 59 | } else { 60 | Object[] var2 = list.toArray(); 61 | int var3 = var2.length; 62 | 63 | for (int var4 = 0; var4 < var3; ++var4) { 64 | Object object = var2[var4]; 65 | notNull(object, name + " element "); 66 | } 67 | } 68 | } 69 | 70 | 71 | public static void positive(int number, String paramName) { 72 | if (number <= 0) { 73 | throw new IllegalArgumentException(paramName + " must be > 0!"); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /retry-common/src/main/java/com/poldroc/retry/common/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.common.util; 2 | 3 | /** 4 | * 字符串工具类 5 | * 6 | * @author Poldroc 7 | * @since 2024/7/11 8 | */ 9 | 10 | public final class StringUtil { 11 | 12 | private StringUtil() { 13 | } 14 | 15 | 16 | /** 17 | * 空字符串 18 | */ 19 | public static final String EMPTY = ""; 20 | 21 | 22 | /** 23 | * 判断字符串是否为空 24 | * 25 | * @param string 字符串 26 | * @return 是否为空 27 | */ 28 | public static boolean isEmpty(String string) { 29 | return null == string || EMPTY.equals(string); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /retry-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.poldroc 8 | roc-retry 9 | 1.1 10 | 11 | 12 | retry-core 13 | The core module of roc-retry. 14 | 15 | 16 | 17 | 8 18 | 8 19 | UTF-8 20 | 21 | 22 | 23 | 24 | ${project.groupId} 25 | retry-api 26 | 27 | 28 | ${project.groupId} 29 | retry-common 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/constant/RetryWaitConst.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.constant; 2 | 3 | /** 4 | * 重试等待时间常量 5 | * 6 | * @author Poldroc 7 | * @since 2024/7/11 8 | */ 9 | 10 | public final class RetryWaitConst { 11 | private RetryWaitConst() { 12 | } 13 | 14 | /** 15 | * 默认基础值 16 | * 1s 17 | */ 18 | public static final long DEFAULT_VALUE_MILLS = 1000L; 19 | 20 | /** 21 | * 最小等待时间 22 | */ 23 | public static final long DEFAULT_MIN_MILLS = 0L; 24 | 25 | /** 26 | * 最大等待时间 27 | * 30min 28 | */ 29 | public static final long DEFAULT_MAX_MILLS = 30 * 60 * 1000L; 30 | 31 | /** 32 | * 增加的毫秒数因数 33 | * 默认为 2S 34 | */ 35 | public static final double INCREASE_MILLS_FACTOR = 2000; 36 | 37 | /** 38 | * 因数 39 | * 默认为黄金分割比 40 | */ 41 | public static final double MULTIPLY_FACTOR = 1.618; 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/context/DefaultRetryContext.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.context; 2 | 3 | import com.poldroc.retry.api.context.RetryContext; 4 | import com.poldroc.retry.api.context.RetryWaitContext; 5 | import com.poldroc.retry.api.core.Retry; 6 | import com.poldroc.retry.api.support.block.RetryBlock; 7 | import com.poldroc.retry.api.support.condition.RetryCondition; 8 | import com.poldroc.retry.api.support.listen.RetryListen; 9 | import com.poldroc.retry.api.support.recover.Recover; 10 | import com.poldroc.retry.api.support.stop.RetryStop; 11 | import com.poldroc.retry.common.annotation.NotThreadSafe; 12 | 13 | import java.util.Collections; 14 | import java.util.List; 15 | import java.util.concurrent.Callable; 16 | 17 | /** 18 | * 默认重试执行上下文 19 | * 20 | * @author Poldroc 21 | * @since 2024/7/11 22 | */ 23 | 24 | @NotThreadSafe 25 | public class DefaultRetryContext implements RetryContext { 26 | 27 | /** 28 | * 重试实现类 29 | */ 30 | private Retry retry; 31 | 32 | /** 33 | * 重试生效条件 34 | */ 35 | private RetryCondition condition; 36 | 37 | /** 38 | * 重试等待上下文 39 | */ 40 | private List> waitContext; 41 | 42 | /** 43 | * 阻塞实现 44 | */ 45 | private RetryBlock block; 46 | 47 | /** 48 | * 停止策略 49 | */ 50 | private RetryStop stop; 51 | 52 | /** 53 | * 可执行的方法 54 | */ 55 | private Callable callable; 56 | 57 | /** 58 | * 监听器 59 | */ 60 | private RetryListen listen; 61 | 62 | /** 63 | * 恢复策略 64 | */ 65 | private Recover recover; 66 | 67 | /** 68 | * 请求参数信息 69 | */ 70 | private Object[] params; 71 | 72 | 73 | @Override 74 | public Retry retry() { 75 | return retry; 76 | } 77 | 78 | public DefaultRetryContext retry(Retry retry) { 79 | this.retry = retry; 80 | return this; 81 | } 82 | 83 | @Override 84 | public RetryCondition condition() { 85 | return condition; 86 | } 87 | 88 | public DefaultRetryContext condition(RetryCondition condition) { 89 | this.condition = condition; 90 | return this; 91 | } 92 | 93 | @Override 94 | public List> waitContext() { 95 | return waitContext; 96 | } 97 | 98 | public DefaultRetryContext waitContext(List> waitContext) { 99 | this.waitContext = waitContext; 100 | return this; 101 | } 102 | 103 | @Override 104 | public RetryBlock block() { 105 | return block; 106 | } 107 | 108 | public DefaultRetryContext block(RetryBlock block) { 109 | this.block = block; 110 | return this; 111 | } 112 | 113 | @Override 114 | public RetryStop stop() { 115 | return stop; 116 | } 117 | 118 | public DefaultRetryContext stop(RetryStop stop) { 119 | this.stop = stop; 120 | return this; 121 | } 122 | 123 | @Override 124 | public Callable callable() { 125 | return callable; 126 | } 127 | 128 | public DefaultRetryContext callable(Callable callable) { 129 | this.callable = callable; 130 | return this; 131 | } 132 | 133 | public DefaultRetryContext retry(Callable callable) { 134 | this.callable = callable; 135 | return this; 136 | } 137 | 138 | @Override 139 | public RetryListen listen() { 140 | return listen; 141 | } 142 | 143 | public DefaultRetryContext listen(RetryListen listen) { 144 | this.listen = listen; 145 | return this; 146 | } 147 | 148 | @Override 149 | public Recover recover() { 150 | return recover; 151 | } 152 | 153 | public DefaultRetryContext recover(Recover recover) { 154 | this.recover = recover; 155 | return this; 156 | } 157 | 158 | @Override 159 | public Object[] params() { 160 | return params; 161 | } 162 | 163 | @Override 164 | public DefaultRetryContext params(Object[] params) { 165 | this.params = params; 166 | return this; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/context/DefaultRetryWaitContext.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.context; 2 | 3 | import com.poldroc.retry.api.context.RetryWaitContext; 4 | import com.poldroc.retry.api.model.AttemptTime; 5 | import com.poldroc.retry.api.model.RetryAttempt; 6 | import com.poldroc.retry.api.support.wait.RetryWait; 7 | 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | /** 12 | * 默认重试等待上下文 13 | * 14 | * @author Poldroc 15 | * @since 2024/7/11 16 | */ 17 | 18 | public class DefaultRetryWaitContext implements RetryWaitContext { 19 | 20 | /** 21 | * 执行结果 22 | */ 23 | private R result; 24 | 25 | /** 26 | * 尝试次数 27 | */ 28 | private int attempt; 29 | 30 | /** 31 | * 尝试次数 32 | */ 33 | private Throwable cause; 34 | 35 | /** 36 | * 消耗时间 37 | */ 38 | private AttemptTime time; 39 | 40 | /** 41 | * 历史信息 42 | */ 43 | private List> history; 44 | 45 | /** 46 | * 基础值 47 | */ 48 | private long value; 49 | 50 | /** 51 | * 最小值 52 | */ 53 | private long min; 54 | 55 | /** 56 | * 最大值 57 | */ 58 | private long max; 59 | 60 | /** 61 | * 变化因子 62 | */ 63 | private double factor; 64 | 65 | /** 66 | * 重试等待类 67 | */ 68 | private Class retryWait; 69 | 70 | /** 71 | * 请求参数 72 | */ 73 | private Object[] params; 74 | 75 | 76 | @Override 77 | public R result() { 78 | return result; 79 | } 80 | 81 | public DefaultRetryWaitContext result(R result) { 82 | this.result = result; 83 | return this; 84 | } 85 | 86 | @Override 87 | public int attempt() { 88 | return attempt; 89 | } 90 | 91 | public DefaultRetryWaitContext attempt(int attempt) { 92 | this.attempt = attempt; 93 | return this; 94 | } 95 | 96 | @Override 97 | public Throwable cause() { 98 | return cause; 99 | } 100 | 101 | public DefaultRetryWaitContext cause(Throwable cause) { 102 | this.cause = cause; 103 | return this; 104 | } 105 | 106 | @Override 107 | public AttemptTime time() { 108 | return time; 109 | } 110 | 111 | public DefaultRetryWaitContext time(AttemptTime time) { 112 | this.time = time; 113 | return this; 114 | } 115 | 116 | @Override 117 | public List> history() { 118 | return history; 119 | } 120 | 121 | public DefaultRetryWaitContext history(List> history) { 122 | this.history = history; 123 | return this; 124 | } 125 | 126 | @Override 127 | public long value() { 128 | return value; 129 | } 130 | 131 | public DefaultRetryWaitContext value(long value) { 132 | this.value = value; 133 | return this; 134 | } 135 | 136 | @Override 137 | public long min() { 138 | return min; 139 | } 140 | 141 | public DefaultRetryWaitContext min(long min) { 142 | this.min = min; 143 | return this; 144 | } 145 | 146 | @Override 147 | public long max() { 148 | return max; 149 | } 150 | 151 | public DefaultRetryWaitContext max(long max) { 152 | this.max = max; 153 | return this; 154 | } 155 | 156 | @Override 157 | public double factor() { 158 | return factor; 159 | } 160 | 161 | public DefaultRetryWaitContext factor(double factor) { 162 | this.factor = factor; 163 | return this; 164 | } 165 | 166 | @Override 167 | public Class retryWait() { 168 | return retryWait; 169 | } 170 | 171 | public DefaultRetryWaitContext retryWait(Class retryWait) { 172 | this.retryWait = retryWait; 173 | return this; 174 | } 175 | 176 | @Override 177 | public Object[] params() { 178 | return params; 179 | } 180 | 181 | public DefaultRetryWaitContext params(Object[] params) { 182 | this.params = params; 183 | return this; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/core/RetryWaiter.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.core; 2 | 3 | import com.poldroc.retry.api.context.RetryWaitContext; 4 | import com.poldroc.retry.api.support.wait.RetryWait; 5 | import com.poldroc.retry.common.annotation.NotThreadSafe; 6 | import com.poldroc.retry.core.constant.RetryWaitConst; 7 | import com.poldroc.retry.core.context.DefaultRetryWaitContext; 8 | import com.poldroc.retry.core.support.wait.ExponentialRetryWait; 9 | import com.poldroc.retry.core.support.wait.IncreaseRetryWait; 10 | import com.poldroc.retry.core.support.wait.NoRetryWait; 11 | 12 | /** 13 | * 重试等待类构造器 14 | * 15 | * @author Poldroc 16 | * @since 2024/7/11 17 | */ 18 | 19 | @NotThreadSafe 20 | public class RetryWaiter { 21 | 22 | /** 23 | * 重试等待类的类型 24 | */ 25 | private Class retryWait = NoRetryWait.class; 26 | 27 | /** 28 | * 默认的等待时间 29 | */ 30 | private long value = RetryWaitConst.DEFAULT_VALUE_MILLS; 31 | 32 | /** 33 | * 最小值 34 | */ 35 | private long min = RetryWaitConst.DEFAULT_MIN_MILLS; 36 | 37 | /** 38 | * 最大值 39 | */ 40 | private long max = RetryWaitConst.DEFAULT_MAX_MILLS; 41 | 42 | /** 43 | * 变化因子 44 | *

45 | * 1. 如果是 {@link com.poldroc.retry.core.support.wait.ExponentialRetryWait} 则为 {@link com.poldroc.retry.core.constant.RetryWaitConst#MULTIPLY_FACTOR} 46 | *

47 | * 2. 如果是 {@link com.poldroc.retry.core.support.wait.IncreaseRetryWait} 则为 {@link com.poldroc.retry.core.constant.RetryWaitConst#INCREASE_MILLS_FACTOR} 48 | */ 49 | private double factor = Double.MIN_VALUE; 50 | 51 | /** 52 | * 构造器私有化 53 | */ 54 | private RetryWaiter() { 55 | } 56 | 57 | /** 58 | * 设置重试等待的对象类型 59 | * 并且设置默认的因子 60 | * @param retryWait 重试等待类 61 | * @param 泛型 62 | * @return 重试等待类 63 | */ 64 | public static RetryWaiter retryWait(Class retryWait) { 65 | RetryWaiter retryWaiter = new RetryWaiter<>(); 66 | retryWaiter.retryWait = retryWait; 67 | if (IncreaseRetryWait.class.equals(retryWait)) { 68 | retryWaiter.factor(RetryWaitConst.INCREASE_MILLS_FACTOR); 69 | } 70 | if (ExponentialRetryWait.class.equals(retryWait)) { 71 | retryWaiter.factor(RetryWaitConst.MULTIPLY_FACTOR); 72 | } 73 | return retryWaiter; 74 | } 75 | 76 | public Class retryWait() { 77 | return retryWait; 78 | } 79 | 80 | public long value() { 81 | return value; 82 | } 83 | 84 | public RetryWaiter value(long value) { 85 | this.value = value; 86 | return this; 87 | } 88 | 89 | public long min() { 90 | return min; 91 | } 92 | 93 | public RetryWaiter min(long min) { 94 | this.min = min; 95 | return this; 96 | } 97 | 98 | public long max() { 99 | return max; 100 | } 101 | 102 | public RetryWaiter max(long max) { 103 | this.max = max; 104 | return this; 105 | } 106 | 107 | public double factor() { 108 | return factor; 109 | } 110 | 111 | public RetryWaiter factor(double factor) { 112 | this.factor = factor; 113 | return this; 114 | } 115 | 116 | /** 117 | * 构建重试等待时间上下文 118 | */ 119 | public RetryWaitContext context() { 120 | return new DefaultRetryWaitContext() 121 | .value(value) 122 | .min(min) 123 | .max(max) 124 | .factor(factor) 125 | .retryWait(retryWait); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/core/Retryer.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.core; 2 | 3 | import com.poldroc.retry.api.context.RetryContext; 4 | import com.poldroc.retry.api.context.RetryWaitContext; 5 | import com.poldroc.retry.api.core.Retry; 6 | import com.poldroc.retry.api.support.block.RetryBlock; 7 | import com.poldroc.retry.api.support.condition.RetryCondition; 8 | import com.poldroc.retry.api.support.listen.RetryListen; 9 | import com.poldroc.retry.api.support.recover.Recover; 10 | import com.poldroc.retry.api.support.stop.RetryStop; 11 | import com.poldroc.retry.common.annotation.NotThreadSafe; 12 | import com.poldroc.retry.common.util.ArgUtil; 13 | import com.poldroc.retry.core.context.DefaultRetryContext; 14 | import com.poldroc.retry.core.core.retry.DefaultRetry; 15 | import com.poldroc.retry.core.support.block.ThreadSleepRetryBlock; 16 | import com.poldroc.retry.core.support.condition.RetryConditions; 17 | import com.poldroc.retry.core.support.listen.NoRetryListen; 18 | import com.poldroc.retry.core.support.recover.NoRecover; 19 | import com.poldroc.retry.core.support.stop.MaxAttemptRetryStop; 20 | import com.poldroc.retry.core.support.wait.NoRetryWait; 21 | 22 | import java.util.Arrays; 23 | import java.util.Collections; 24 | import java.util.List; 25 | import java.util.concurrent.Callable; 26 | 27 | /** 28 | * 引导核心类 29 | * 30 | * @author Poldroc 31 | * @since 2024/7/13 32 | */ 33 | @NotThreadSafe 34 | public class Retryer implements Retry { 35 | 36 | /** 37 | * 待执行的方法 38 | */ 39 | private Callable callable; 40 | 41 | /** 42 | * 重试实现类 43 | * 1. 不推荐用户自定义,但是暴露出来。 44 | */ 45 | private Retry retry = DefaultRetry.getInstance(); 46 | 47 | /** 48 | * 执行重试的条件 49 | * 1. 默认在遇到异常的时候进行重试 50 | * 2. 支持多个条件,任意一个满足则满足。如果用户有更特殊的需求,应该自己定义。 51 | */ 52 | private RetryCondition condition = RetryConditions.hasExceptionCause(); 53 | 54 | /** 55 | * 阻塞的方式 56 | * 1. 默认采用线程沉睡的方式 57 | */ 58 | private RetryBlock block = ThreadSleepRetryBlock.getInstance(); 59 | 60 | /** 61 | * 重试停止的条件 62 | * 1. 默认最多重试 3 次 63 | */ 64 | private RetryStop stop = new MaxAttemptRetryStop(3); 65 | 66 | /** 67 | * 监听器 68 | * 1. 默认不进行任何操作 69 | */ 70 | private RetryListen listen = NoRetryListen.getInstance(); 71 | 72 | /** 73 | * 恢复策略 74 | * 1. 默认不进行任何操作 75 | */ 76 | private Recover recover = NoRecover.getInstance(); 77 | 78 | /** 79 | * 重试等待上下文 80 | */ 81 | private List> waitContexts = Collections.singletonList(RetryWaiter.retryWait(NoRetryWait.class).context()); 82 | 83 | 84 | /** 85 | * 创建一个对象实例 86 | * 87 | * @param 泛型 88 | * @return 实例 89 | */ 90 | public static Retryer newInstance() { 91 | return new Retryer<>(); 92 | } 93 | 94 | /** 95 | * 1. 设置待处理的方法类 96 | * 2. 返回引导类 instance 97 | * 98 | * @param callable 待处理的方法 99 | * @return this 100 | */ 101 | public Retryer callable(final Callable callable) { 102 | ArgUtil.notNull(callable, "callable"); 103 | 104 | this.callable = callable; 105 | return this; 106 | } 107 | 108 | 109 | /** 110 | * 设置重试实现类 111 | * 112 | * @param retry 重试实现类 113 | * @return this 114 | */ 115 | public Retryer retry(Retry retry) { 116 | this.retry = retry; 117 | return this; 118 | } 119 | 120 | /** 121 | * 重试生效条件 122 | * 123 | * @param condition 生效条件 124 | * @return this 125 | */ 126 | public Retryer condition(RetryCondition condition) { 127 | ArgUtil.notNull(condition, "condition"); 128 | 129 | this.condition = condition; 130 | return this; 131 | } 132 | 133 | 134 | /** 135 | * 重试等待上下文 136 | * 137 | * @param retryWaitContexts 重试等待上下文数组 138 | * @return 重试等待上下文 139 | */ 140 | public Retryer retryWaitContext(RetryWaitContext... retryWaitContexts) { 141 | ArgUtil.notEmpty(retryWaitContexts, "retryWaitContexts"); 142 | this.waitContexts = Arrays.asList(retryWaitContexts); 143 | return this; 144 | } 145 | 146 | public Retryer retryWaitContext(List> retryWaitContexts) { 147 | ArgUtil.notEmpty(retryWaitContexts, "retryWaitContexts"); 148 | this.waitContexts = retryWaitContexts; 149 | return this; 150 | } 151 | 152 | /** 153 | * 最大尝试次数 154 | * 155 | * @param maxAttempt 最大尝试次数 156 | * @return this 157 | */ 158 | public Retryer maxAttempt(final int maxAttempt) { 159 | ArgUtil.positive(maxAttempt, "maxAttempt"); 160 | 161 | this.stop = new MaxAttemptRetryStop(maxAttempt); 162 | return this; 163 | } 164 | 165 | /** 166 | * 设置阻塞策略 167 | * 168 | * @param block 阻塞策略 169 | * @return this 170 | */ 171 | private Retryer block(RetryBlock block) { 172 | ArgUtil.notNull(block, "block"); 173 | 174 | this.block = block; 175 | return this; 176 | } 177 | 178 | /** 179 | * 设置停止策略 180 | * 181 | * @param stop 停止策略 182 | * @return this 183 | */ 184 | private Retryer stop(RetryStop stop) { 185 | ArgUtil.notNull(stop, "stop"); 186 | 187 | this.stop = stop; 188 | return this; 189 | } 190 | 191 | /** 192 | * 设置监听 193 | * 194 | * @param listen 监听 195 | * @return this 196 | */ 197 | public Retryer listen(RetryListen listen) { 198 | ArgUtil.notNull(listen, "listen"); 199 | 200 | this.listen = listen; 201 | return this; 202 | } 203 | 204 | /** 205 | * 设置恢复策略 206 | * 207 | * @param recover 恢复策略 208 | * @return this 209 | */ 210 | public Retryer recover(Recover recover) { 211 | ArgUtil.notNull(recover, "recover"); 212 | 213 | this.recover = recover; 214 | return this; 215 | } 216 | 217 | 218 | /** 219 | * 构建重试上下文 220 | * 221 | * @return 重试上下文 222 | */ 223 | public RetryContext context() { 224 | // 初始化 225 | return new DefaultRetryContext() 226 | .callable(callable) 227 | .waitContext(waitContexts) 228 | .block(block) 229 | .stop(stop) 230 | .condition(condition) 231 | .listen(listen) 232 | .recover(recover) 233 | .retry(retry); 234 | } 235 | 236 | 237 | /** 238 | * 重试执行 239 | * 240 | * @return 执行结果 241 | */ 242 | public R retryCall() { 243 | RetryContext context = context(); 244 | return context().retry().retryCall(context); 245 | } 246 | 247 | /** 248 | * 重试执行 249 | * 250 | * @param context 执行上下文 251 | * @return 执行结果 252 | */ 253 | @Override 254 | public R retryCall(RetryContext context) { 255 | return context.retry().retryCall(context); 256 | // return context().retry().retryCall(context); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/core/retry/DefaultRetry.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.core.retry; 2 | 3 | import com.poldroc.retry.api.context.RetryContext; 4 | import com.poldroc.retry.api.context.RetryWaitContext; 5 | import com.poldroc.retry.api.core.Retry; 6 | import com.poldroc.retry.api.exception.RetryException; 7 | import com.poldroc.retry.api.model.RetryAttempt; 8 | import com.poldroc.retry.api.model.WaitTime; 9 | import com.poldroc.retry.api.support.block.RetryBlock; 10 | import com.poldroc.retry.api.support.condition.RetryCondition; 11 | import com.poldroc.retry.api.support.listen.RetryListen; 12 | import com.poldroc.retry.api.support.recover.Recover; 13 | import com.poldroc.retry.api.support.stop.RetryStop; 14 | import com.poldroc.retry.api.support.wait.RetryWait; 15 | import com.poldroc.retry.common.annotation.ThreadSafe; 16 | import com.poldroc.retry.common.support.instance.impl.InstanceFactory; 17 | import com.poldroc.retry.core.context.DefaultRetryWaitContext; 18 | import com.poldroc.retry.core.model.DefaultAttemptTime; 19 | import com.poldroc.retry.core.model.DefaultRetryAttempt; 20 | import com.poldroc.retry.core.model.DefaultWaitTime; 21 | 22 | import java.lang.reflect.InvocationTargetException; 23 | import java.util.ArrayList; 24 | import java.util.Date; 25 | import java.util.List; 26 | import java.util.concurrent.Callable; 27 | import java.util.concurrent.TimeUnit; 28 | 29 | /** 30 | * 默认的重试实现 31 | * 32 | * @author Poldroc 33 | * @since 2024/7/11 34 | */ 35 | 36 | @ThreadSafe 37 | public class DefaultRetry implements Retry { 38 | 39 | /** 40 | * 获取单例 41 | * 42 | * @return 单例 43 | */ 44 | public static DefaultRetry getInstance() { 45 | return InstanceFactory.getInstance().singleton(DefaultRetry.class); 46 | } 47 | 48 | /** 49 | * 重试调用 50 | *

51 | * 1. 执行方法体 52 | * 2. 判断是否需要重试 -> 符合{@link RetryCondition} 重试条件 && 不符合{@link RetryStop} 重试停止条件 53 | * 3. 满足重试条件,并且不满足重试停止条件 -> 计算等待时间 -> 阻塞 -> 重试次数+1 -> 添加重试历史 -> 重新执行 -> 触发监听器 54 | * 4. 满足重试条件,但是满足重试停止条件 -> 触发恢复策略 55 | * 5. 最后一次重试还是有异常,直接抛出异常 56 | * 6. 返回最后一次尝试的结果 57 | * 58 | * @param context 执行上下文 59 | * @return 60 | */ 61 | @Override 62 | public R retryCall(RetryContext context) { 63 | List> history = new ArrayList<>(); 64 | int attempts = 1; 65 | final Callable callable = context.callable(); 66 | RetryAttempt retryAttempt = execute(callable, attempts, history, context); 67 | 68 | final List> waitContextList = context.waitContext(); 69 | final RetryCondition retryCondition = context.condition(); 70 | final RetryStop retryStop = context.stop(); 71 | final RetryBlock retryBlock = context.block(); 72 | final RetryListen retryListen = context.listen(); 73 | 74 | // 触发执行的 condition 并且 不触发 stop 策略 就进行重试 75 | while (retryCondition.condition(retryAttempt) && !retryStop.stop(retryAttempt)) { 76 | // 线程阻塞 77 | WaitTime waitTime = calcWaitTime(waitContextList, retryAttempt); 78 | retryBlock.block(waitTime); 79 | // 每一次执行会更新 executeResult 80 | attempts++; 81 | history.add(retryAttempt); 82 | retryAttempt = this.execute(callable, attempts, history, context); 83 | 84 | // 触发监听器 85 | retryListen.listen(retryAttempt); 86 | } 87 | 88 | // 仍然满足重试条件,但是满足重试停止条件 89 | if (retryCondition.condition(retryAttempt) && retryStop.stop(retryAttempt)) { 90 | // 触发恢复策略 91 | final Recover recover = context.recover(); 92 | recover.recover(retryAttempt); 93 | } 94 | 95 | // 最后一次还是有异常,直接抛出异常 96 | final Throwable throwable = retryAttempt.cause(); 97 | if (throwable != null) { 98 | // 1. 运行时异常,则直接抛出 99 | // 2. 非运行时异常,则包装成为 RetryException 100 | if (throwable instanceof RuntimeException) { 101 | throw (RuntimeException) throwable; 102 | } 103 | throw new RetryException(retryAttempt.cause()); 104 | } 105 | // 返回最后一次尝试的结果 106 | return retryAttempt.result(); 107 | } 108 | 109 | /** 110 | * 构建等待时间 111 | * 112 | * @param waitContextList 等待上下文列表 113 | * @param retryAttempt 重试信息 114 | * @return 等待时间毫秒 115 | */ 116 | private WaitTime calcWaitTime(final List> waitContextList, 117 | final RetryAttempt retryAttempt) { 118 | long totalTimeMills = 0; 119 | for (RetryWaitContext context : waitContextList) { 120 | RetryWait retryWait = (RetryWait) InstanceFactory.getInstance().threadSafe(context.retryWait()); 121 | final RetryWaitContext retryWaitContext = buildRetryWaitContext(context, retryAttempt); 122 | WaitTime waitTime = retryWait.waitTime(retryWaitContext); 123 | totalTimeMills += TimeUnit.MILLISECONDS.convert(waitTime.time(), waitTime.unit()); 124 | } 125 | return new DefaultWaitTime(totalTimeMills); 126 | } 127 | 128 | private RetryWaitContext buildRetryWaitContext(RetryWaitContext waitContext, RetryAttempt retryAttempt) { 129 | DefaultRetryWaitContext context = (DefaultRetryWaitContext) waitContext; 130 | return context.attempt(retryAttempt.attempt()) 131 | .cause(retryAttempt.cause()) 132 | .result(retryAttempt.result()) 133 | .time(retryAttempt.time()) 134 | .history(retryAttempt.history()) 135 | .params(retryAttempt.params()); 136 | 137 | } 138 | 139 | private RetryAttempt execute(final Callable callable, 140 | final int attempts, 141 | final List> history, 142 | final RetryContext context) { 143 | 144 | Date startTime = new Date(); 145 | Throwable throwable = null; 146 | R result = null; 147 | try { 148 | result = callable.call(); 149 | } catch (Exception e) { 150 | throwable = getActualThrowable(e); 151 | } 152 | Date endTime = new Date(); 153 | DefaultAttemptTime attemptTime = new DefaultAttemptTime() 154 | .startTime(startTime) 155 | .endTime(endTime) 156 | .costTimeInMills(endTime.getTime() - startTime.getTime()); 157 | return new DefaultRetryAttempt() 158 | .result(result) 159 | .attempt(attempts) 160 | .cause(throwable) 161 | .time(attemptTime) 162 | // 设置请求入参,主要用于回调等使用。 163 | .params(context.params()) 164 | .history(history); 165 | } 166 | 167 | /** 168 | * 获取实际的异常信息 169 | * 170 | * @param throwable 异常信息 171 | * @return 实际的异常信息 172 | */ 173 | private Throwable getActualThrowable(Throwable throwable) { 174 | if (InvocationTargetException.class.equals(throwable.getClass())) { 175 | InvocationTargetException exception = (InvocationTargetException) throwable; 176 | return exception.getTargetException(); 177 | } else { 178 | return throwable; 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/model/DefaultAttemptTime.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.model; 2 | 3 | import com.poldroc.retry.api.model.AttemptTime; 4 | import com.poldroc.retry.common.annotation.NotThreadSafe; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * 尝试执行的时候消耗时间 10 | * @author Poldroc 11 | * @since 2024/7/11 12 | */ 13 | 14 | @NotThreadSafe 15 | public class DefaultAttemptTime implements AttemptTime { 16 | 17 | /** 18 | * 开始时间 19 | */ 20 | private Date startTime; 21 | /** 22 | * 结束时间 23 | */ 24 | private Date endTime; 25 | /** 26 | * 消耗的时间 27 | */ 28 | private long costTimeInMills; 29 | 30 | @Override 31 | public Date startTime() { 32 | return startTime; 33 | } 34 | 35 | public DefaultAttemptTime startTime(Date startTime) { 36 | this.startTime = startTime; 37 | return this; 38 | } 39 | 40 | @Override 41 | public Date endTime() { 42 | return endTime; 43 | } 44 | 45 | public DefaultAttemptTime endTime(Date endTime) { 46 | this.endTime = endTime; 47 | return this; 48 | } 49 | 50 | @Override 51 | public long costTimeInMills() { 52 | return costTimeInMills; 53 | } 54 | 55 | public DefaultAttemptTime costTimeInMills(long costTimeInMills) { 56 | this.costTimeInMills = costTimeInMills; 57 | return this; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return "DefaultAttemptTime{" + 63 | "startTime=" + startTime + 64 | ", endTime=" + endTime + 65 | ", costTimeInMills=" + costTimeInMills + 66 | '}'; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/model/DefaultRetryAttempt.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.model; 2 | 3 | import com.poldroc.retry.api.model.AttemptTime; 4 | import com.poldroc.retry.api.model.RetryAttempt; 5 | import com.poldroc.retry.common.annotation.NotThreadSafe; 6 | 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | /** 11 | * 默认重试信息 12 | * 13 | * @author Poldroc 14 | * @since 2024/7/11 15 | */ 16 | 17 | @NotThreadSafe 18 | public class DefaultRetryAttempt implements RetryAttempt { 19 | 20 | /** 21 | * 执行结果 22 | */ 23 | private R result; 24 | 25 | /** 26 | * 尝试次数 27 | */ 28 | private int attempt; 29 | 30 | /** 31 | * 异常信息 32 | */ 33 | private Throwable cause; 34 | 35 | /** 36 | * 消耗时间 37 | */ 38 | private AttemptTime time; 39 | 40 | /** 41 | * 历史信息 42 | */ 43 | private List> history; 44 | 45 | /** 46 | * 请求参数 47 | * 48 | * @since 0.1.0 49 | */ 50 | private Object[] params; 51 | 52 | @Override 53 | public R result() { 54 | return result; 55 | } 56 | 57 | public DefaultRetryAttempt result(R result) { 58 | this.result = result; 59 | return this; 60 | } 61 | 62 | @Override 63 | public int attempt() { 64 | return attempt; 65 | } 66 | 67 | public DefaultRetryAttempt attempt(int attempt) { 68 | this.attempt = attempt; 69 | return this; 70 | } 71 | 72 | @Override 73 | public Throwable cause() { 74 | return cause; 75 | } 76 | 77 | public DefaultRetryAttempt cause(Throwable cause) { 78 | this.cause = cause; 79 | return this; 80 | } 81 | 82 | @Override 83 | public AttemptTime time() { 84 | return time; 85 | } 86 | 87 | public DefaultRetryAttempt time(AttemptTime time) { 88 | this.time = time; 89 | return this; 90 | } 91 | 92 | @Override 93 | public List> history() { 94 | return history; 95 | } 96 | 97 | public DefaultRetryAttempt history(List> history) { 98 | this.history = history; 99 | return this; 100 | } 101 | 102 | @Override 103 | public Object[] params() { 104 | return params; 105 | } 106 | 107 | public DefaultRetryAttempt params(Object[] params) { 108 | this.params = params; 109 | return this; 110 | } 111 | 112 | @Override 113 | public String toString() { 114 | return "DefaultRetryAttempt{" + 115 | "result=" + result + 116 | ", attempt=" + attempt + 117 | ", cause=" + cause + 118 | ", time=" + time + 119 | ", params=" + Arrays.toString(params) + 120 | '}'; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/model/DefaultWaitTime.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.model; 2 | 3 | import com.poldroc.retry.api.model.WaitTime; 4 | import com.poldroc.retry.common.annotation.ThreadSafe; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | /** 8 | * 默认等待时间 9 | * @author Poldroc 10 | * @since 2024/7/11 11 | */ 12 | 13 | @ThreadSafe 14 | public class DefaultWaitTime implements WaitTime { 15 | 16 | /** 17 | * 等待时间 18 | */ 19 | private final long time; 20 | 21 | /** 22 | * 时间单位 23 | */ 24 | private final TimeUnit unit; 25 | 26 | public DefaultWaitTime(long time) { 27 | this.time = time; 28 | this.unit = TimeUnit.MILLISECONDS; 29 | } 30 | 31 | public DefaultWaitTime(long time, TimeUnit unit) { 32 | this.time = time; 33 | this.unit = unit; 34 | } 35 | 36 | @Override 37 | public long time() { 38 | return this.time; 39 | } 40 | 41 | @Override 42 | public TimeUnit unit() { 43 | return this.unit; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/block/ThreadSleepRetryBlock.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.block; 2 | 3 | import com.poldroc.retry.api.exception.RetryException; 4 | import com.poldroc.retry.api.model.WaitTime; 5 | import com.poldroc.retry.api.support.block.RetryBlock; 6 | import com.poldroc.retry.common.annotation.ThreadSafe; 7 | import com.poldroc.retry.common.support.instance.impl.InstanceFactory; 8 | 9 | /** 10 | * 线程睡眠的阻塞方法 11 | * @author Poldroc 12 | * @since 2024/7/11 13 | */ 14 | @ThreadSafe 15 | public class ThreadSleepRetryBlock implements RetryBlock { 16 | 17 | /** 18 | * 获取单例 19 | * @return 获取单例 20 | */ 21 | public static RetryBlock getInstance() { 22 | return InstanceFactory.getInstance().singleton(ThreadSleepRetryBlock.class); 23 | } 24 | 25 | @Override 26 | public void block(WaitTime waitTime) { 27 | try { 28 | waitTime.unit().sleep(waitTime.time()); 29 | } catch (InterruptedException e) { 30 | // 恢复状态 31 | Thread.currentThread().interrupt(); 32 | throw new RetryException(e); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/condition/AbstractCauseRetryCondition.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.condition; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.condition.RetryCondition; 5 | import com.poldroc.retry.common.annotation.ThreadSafe; 6 | 7 | /** 8 | * 根据异常进行重试的条件 9 | * 10 | * @author Poldroc 11 | * @since 2024/7/12 12 | */ 13 | @ThreadSafe 14 | public abstract class AbstractCauseRetryCondition implements RetryCondition { 15 | @Override 16 | public boolean condition(RetryAttempt retryAttempt) { 17 | return causeCondition(retryAttempt.cause()); 18 | } 19 | 20 | /** 21 | * 对异常信息进行判断 22 | * 1. 用户可以判定是否有异常 23 | * 24 | * @param throwable 异常信息 25 | * @return 对异常信息进行判断 26 | */ 27 | protected abstract boolean causeCondition(final Throwable throwable); 28 | 29 | /** 30 | * 判断是否有异常信息 31 | * 1. 有,返回 true 32 | * 2. 无,返回 false 33 | * 34 | * @param throwable 异常信息 35 | * @return 是否有异常信息 36 | */ 37 | protected boolean hasException(final Throwable throwable) { 38 | return throwable != null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/condition/AbstractResultRetryCondition.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.condition; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.condition.RetryCondition; 5 | import com.poldroc.retry.common.annotation.ThreadSafe; 6 | 7 | /** 8 | * 根据结果进行重试的条件 9 | * 10 | * @author Poldroc 11 | * @since 2024/7/12 12 | */ 13 | @ThreadSafe 14 | public abstract class AbstractResultRetryCondition implements RetryCondition { 15 | @Override 16 | public boolean condition(RetryAttempt retryAttempt) { 17 | return resultCondition(retryAttempt.result()); 18 | } 19 | 20 | /** 21 | * 对结果进行判断 22 | * 23 | * @param result 结果信息 24 | * @return 对结果进行判断 25 | */ 26 | protected abstract boolean resultCondition(final R result); 27 | 28 | 29 | /** 30 | * 判断是否有结果信息 31 | * 1. 有,返回 true 32 | * 2. 无,返回 false 33 | * 34 | * @param result 返回对象 35 | * @return 是否有结果 36 | */ 37 | protected boolean hasResult(final R result) { 38 | return result != null; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/condition/AbstractRetryConditionInit.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.condition; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.condition.RetryCondition; 5 | import com.poldroc.retry.common.annotation.ThreadSafe; 6 | 7 | import java.util.LinkedList; 8 | 9 | /** 10 | * 重试条件初始化类 11 | * 满足任意一个条件即可 12 | * 13 | * @author Poldroc 14 | * @since 2024/7/12 15 | */ 16 | @ThreadSafe 17 | public abstract class AbstractRetryConditionInit implements RetryCondition { 18 | @Override 19 | public boolean condition(RetryAttempt retryAttempt) { 20 | LinkedList conditions = new LinkedList<>(); 21 | this.init(conditions, retryAttempt); 22 | // 判断 23 | for (RetryCondition condition : conditions) { 24 | if (condition.condition(retryAttempt)) { 25 | return true; 26 | } 27 | } 28 | return false; 29 | } 30 | 31 | 32 | /** 33 | * 初始化列表 34 | * 35 | * @param pipeline 当前列表泳道 36 | * @param retryAttempt 执行信息 37 | */ 38 | protected abstract void init(final LinkedList pipeline, 39 | final RetryAttempt retryAttempt); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/condition/AbstractTimeRetryCondition.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.condition; 2 | 3 | import com.poldroc.retry.api.model.AttemptTime; 4 | import com.poldroc.retry.api.model.RetryAttempt; 5 | import com.poldroc.retry.api.support.condition.RetryCondition; 6 | import com.poldroc.retry.common.annotation.ThreadSafe; 7 | 8 | /** 9 | * 根据时间进行重试的抽象类 10 | * 11 | * @author Poldroc 12 | * @since 2024/7/12 13 | */ 14 | 15 | @ThreadSafe 16 | public abstract class AbstractTimeRetryCondition implements RetryCondition { 17 | 18 | @Override 19 | public boolean condition(RetryAttempt retryAttempt) { 20 | return timeCondition(retryAttempt.time()); 21 | } 22 | 23 | /** 24 | * 对消耗时间信息进行判断 25 | * 1. 用户可以判定是执行重试 26 | * 2. 比如任务执行的时间过长,过者任务执行的时间不在预期的时间范围内 27 | * 28 | * @param attemptTime 时间信息 29 | * @return 对消耗时间信息进行判断 30 | */ 31 | protected abstract boolean timeCondition(final AttemptTime attemptTime); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/condition/AlwaysFalseRetryCondition.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.condition; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.condition.RetryCondition; 5 | import com.poldroc.retry.common.annotation.ThreadSafe; 6 | 7 | /** 8 | * 恒为假重试条件 9 | * 10 | * @author Poldroc 11 | * @since 2024/7/12 12 | */ 13 | 14 | @ThreadSafe 15 | public class AlwaysFalseRetryCondition implements RetryCondition { 16 | 17 | /** 18 | * 内部静态类 19 | */ 20 | private static class AlwaysFalseRetryConditionHolder { 21 | private static final AlwaysFalseRetryCondition INSTANCE = new AlwaysFalseRetryCondition(); 22 | } 23 | 24 | /** 25 | * 获取单例 26 | * 27 | * @return 单例 28 | */ 29 | public static RetryCondition getInstance() { 30 | return AlwaysFalseRetryConditionHolder.INSTANCE; 31 | } 32 | 33 | @Override 34 | public boolean condition(RetryAttempt retryAttempt) { 35 | return false; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/condition/AlwaysTrueRetryCondition.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.condition; 2 | 3 | 4 | import com.poldroc.retry.api.model.RetryAttempt; 5 | import com.poldroc.retry.api.support.condition.RetryCondition; 6 | import com.poldroc.retry.common.annotation.ThreadSafe; 7 | 8 | /** 9 | * 恒为真重试条件 10 | * 11 | * @author Poldroc 12 | * @since 2024/7/12 13 | */ 14 | 15 | @ThreadSafe 16 | public class AlwaysTrueRetryCondition implements RetryCondition { 17 | 18 | /** 19 | * 内部静态类 20 | */ 21 | private static class AlwaysTrueRetryConditionHolder { 22 | private static final AlwaysTrueRetryCondition INSTANCE = new AlwaysTrueRetryCondition(); 23 | } 24 | 25 | /** 26 | * 获取单例 27 | * 28 | * @return 单例 29 | */ 30 | public static RetryCondition getInstance() { 31 | return AlwaysTrueRetryConditionHolder.INSTANCE; 32 | } 33 | 34 | @Override 35 | public boolean condition(RetryAttempt retryAttempt) { 36 | return true; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/condition/ExceptionCauseRetryCondition.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.condition; 2 | 3 | import com.poldroc.retry.common.annotation.ThreadSafe; 4 | 5 | /** 6 | * 有异常则触发重试 7 | * 8 | * @author Poldroc 9 | * @since 2024/7/12 10 | */ 11 | @ThreadSafe 12 | public class ExceptionCauseRetryCondition extends AbstractCauseRetryCondition { 13 | @Override 14 | protected boolean causeCondition(Throwable throwable) { 15 | return hasException(throwable); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/condition/NotNullResultRetryCondition.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.condition; 2 | 3 | import com.poldroc.retry.common.annotation.ThreadSafe; 4 | 5 | /** 6 | * 非空结果重试条件 7 | * 8 | * @author Poldroc 9 | * @since 2024/7/12 10 | */ 11 | 12 | @ThreadSafe 13 | public class NotNullResultRetryCondition extends AbstractResultRetryCondition { 14 | @Override 15 | protected boolean resultCondition(R result) { 16 | return hasResult(result); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/condition/NullResultRetryCondition.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.condition; 2 | /** 3 | * 空结果则触发重试 4 | * @author Poldroc 5 | * @since 2024/7/12 6 | */ 7 | 8 | public class NullResultRetryCondition extends AbstractResultRetryCondition { 9 | 10 | @Override 11 | protected boolean resultCondition(R result) { 12 | return !hasResult(result); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/condition/RetryConditions.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.condition; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.condition.RetryCondition; 5 | import com.poldroc.retry.common.support.instance.impl.InstanceFactory; 6 | 7 | import java.util.LinkedList; 8 | 9 | /** 10 | * 重试条件工具类 11 | * 12 | * @author Poldroc 13 | * @since 2024/7/12 14 | */ 15 | 16 | public class RetryConditions { 17 | private RetryConditions() { 18 | } 19 | 20 | /** 21 | * 结果为空 22 | * 23 | * @param 单例 24 | * @return 结果为空 25 | */ 26 | public static RetryCondition isNullResult() { 27 | return InstanceFactory.getInstance().singleton(NullResultRetryCondition.class); 28 | } 29 | 30 | /** 31 | * 结果不为空 32 | * 33 | * @param 单例 34 | * @return 结果为空 35 | */ 36 | public static RetryCondition isNotNullResult() { 37 | return InstanceFactory.getInstance().singleton(NotNullResultRetryCondition.class); 38 | } 39 | 40 | 41 | /** 42 | * 结果等于预期值 43 | * 注意:null 值不等于任何值。 44 | * 45 | * @param excepted 预期值 46 | * @param 单例 47 | * @return 结果为空 48 | */ 49 | public static RetryCondition isEqualsResult(final R excepted) { 50 | return new AbstractResultRetryCondition() { 51 | @Override 52 | protected boolean resultCondition(R result) { 53 | if (result == null) { 54 | return false; 55 | } 56 | return result.equals(excepted); 57 | } 58 | }; 59 | } 60 | 61 | 62 | /** 63 | * 结果不等于预期值 64 | * 65 | * @param excepted 预期值 66 | * @param 单例 67 | * @return 结果为空 68 | */ 69 | public static RetryCondition isNotEqualsResult(final R excepted) { 70 | return new AbstractResultRetryCondition() { 71 | @Override 72 | protected boolean resultCondition(R result) { 73 | if (result == null) { 74 | return true; 75 | } 76 | return !result.equals(excepted); 77 | } 78 | }; 79 | } 80 | 81 | /** 82 | * 程序执行过程中遇到异常 83 | * 84 | * @return 重试条件 85 | */ 86 | public static RetryCondition hasExceptionCause() { 87 | return InstanceFactory.getInstance().singleton(ExceptionCauseRetryCondition.class); 88 | } 89 | 90 | /** 91 | * 是预期的异常类型 92 | * 93 | * @param exceptionClass 异常类型 94 | * @return 重试条件 95 | */ 96 | public static RetryCondition isExceptionCause(final Class exceptionClass) { 97 | return new AbstractCauseRetryCondition() { 98 | @Override 99 | protected boolean causeCondition(Throwable throwable) { 100 | return exceptionClass.isAssignableFrom(throwable.getClass()); 101 | } 102 | }; 103 | } 104 | 105 | /** 106 | * 多条件整合 107 | */ 108 | public static RetryCondition conditions(final RetryCondition... conditions) { 109 | if (conditions == null || conditions.length == 0) { 110 | return AlwaysFalseRetryCondition.getInstance(); 111 | } 112 | return new AbstractRetryConditionInit() { 113 | @Override 114 | protected void init(LinkedList pipeline, RetryAttempt retryAttempt) { 115 | for (RetryCondition condition : conditions) { 116 | pipeline.addLast(condition); 117 | } 118 | } 119 | }; 120 | } 121 | 122 | 123 | } 124 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/listen/AbstractRetryListenInit.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.listen; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.listen.RetryListen; 5 | import com.poldroc.retry.common.annotation.ThreadSafe; 6 | 7 | import java.util.LinkedList; 8 | 9 | /** 10 | * 监听器初始化 11 | * 12 | * @author Poldroc 13 | * @since 2024/7/12 14 | */ 15 | 16 | @ThreadSafe 17 | public abstract class AbstractRetryListenInit implements RetryListen { 18 | @Override 19 | public void listen(RetryAttempt attempt) { 20 | LinkedList listens = new LinkedList<>(); 21 | this.init(listens, attempt); 22 | // 执行 23 | for (RetryListen listen : listens) { 24 | listen.listen(attempt); 25 | } 26 | } 27 | 28 | protected abstract void init(final LinkedList pipeline, final RetryAttempt attempt); 29 | } 30 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/listen/NoRetryListen.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.listen; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.listen.RetryListen; 5 | import com.poldroc.retry.common.annotation.ThreadSafe; 6 | import com.poldroc.retry.common.support.instance.impl.InstanceFactory; 7 | /** 8 | * 不进行任何监听动作 9 | * @author Poldroc 10 | * @since 2024/7/12 11 | */ 12 | 13 | @ThreadSafe 14 | public class NoRetryListen implements RetryListen { 15 | 16 | /** 17 | * 获取单例 18 | * @return 单例 19 | */ 20 | public static RetryListen getInstance() { 21 | return InstanceFactory.getInstance().singleton(NoRetryListen.class); 22 | } 23 | 24 | @Override 25 | public void listen(RetryAttempt attempt) { 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/listen/RetryListens.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.listen; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.listen.RetryListen; 5 | 6 | import java.util.LinkedList; 7 | 8 | /** 9 | * 监听器工具类 10 | * 11 | * @author Poldroc 12 | * @since 2024/7/12 13 | */ 14 | 15 | public class RetryListens { 16 | 17 | private RetryListens() { 18 | } 19 | 20 | /** 21 | * 不进行任何监听动作 22 | * 23 | * @return 监听器 24 | */ 25 | public static RetryListen noListen() { 26 | return NoRetryListen.getInstance(); 27 | } 28 | 29 | /** 30 | * 指定多个监听器 31 | * 32 | * @param retryListens 多个监听器信息 33 | * @return 监听器 34 | */ 35 | public static RetryListen listens(final RetryListen... retryListens) { 36 | if (null == retryListens || retryListens.length == 0) { 37 | return noListen(); 38 | } 39 | return new AbstractRetryListenInit() { 40 | @Override 41 | protected void init(LinkedList pipeline, RetryAttempt attempt) { 42 | for (RetryListen retryListen : retryListens) { 43 | pipeline.addLast(retryListen); 44 | } 45 | } 46 | }; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/recover/NoRecover.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.recover; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.recover.Recover; 5 | import com.poldroc.retry.common.annotation.ThreadSafe; 6 | import com.poldroc.retry.common.support.instance.impl.InstanceFactory; 7 | 8 | /** 9 | * 没有任何恢复操作 10 | * @author Poldroc 11 | * @since 2024/9/1 12 | */ 13 | 14 | @ThreadSafe 15 | public class NoRecover implements Recover { 16 | 17 | /** 18 | * 获取一个单例示例 19 | * @return 单例示例 20 | */ 21 | public static Recover getInstance() { 22 | return InstanceFactory.getInstance().singleton(NoRecover.class); 23 | } 24 | 25 | @Override 26 | public void recover(RetryAttempt retryAttempt) { 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/recover/Recovers.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.recover; 2 | 3 | import com.poldroc.retry.api.support.recover.Recover; 4 | /** 5 | * 没有任何恢复操作 6 | * @author Poldroc 7 | * @since 2024/9/1 8 | */ 9 | 10 | public final class Recovers { 11 | 12 | private Recovers(){} 13 | 14 | /** 15 | * 没有任何恢复操作实例 16 | * @return recover 实例 17 | */ 18 | public static Recover noRecover() { 19 | return NoRecover.getInstance(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/stop/MaxAttemptRetryStop.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.stop; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.stop.RetryStop; 5 | /** 6 | * 最大尝试次数终止策略 7 | * @author Poldroc 8 | * @since 2024/7/11 9 | */ 10 | 11 | public class MaxAttemptRetryStop implements RetryStop { 12 | 13 | /** 14 | * 最大尝试次数 15 | */ 16 | private final int maxAttempt; 17 | 18 | public MaxAttemptRetryStop(int maxAttempt) { 19 | if (maxAttempt <= 0) { 20 | throw new IllegalArgumentException("MaxAttempt must be positive"); 21 | } 22 | this.maxAttempt = maxAttempt; 23 | } 24 | 25 | @Override 26 | public boolean stop(RetryAttempt attempt) { 27 | return attempt.attempt() >= maxAttempt; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/wait/AbstractRetryWait.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.wait; 2 | 3 | import com.poldroc.retry.api.model.WaitTime; 4 | import com.poldroc.retry.api.support.wait.RetryWait; 5 | import com.poldroc.retry.core.model.DefaultWaitTime; 6 | 7 | /** 8 | * 默认重试时间等待 9 | * 10 | * @author Poldroc 11 | * @since 2024/7/11 12 | */ 13 | 14 | public abstract class AbstractRetryWait implements RetryWait { 15 | 16 | /** 17 | * 修正时间范围 18 | * 防止时间超出范围 19 | * @param timeMills 结果 20 | * @param min 最小值 21 | * @param max 最大值 22 | * @return 修正范围 23 | */ 24 | protected WaitTime rangeCorrect(final long timeMills, final long min, final long max) { 25 | long resultMills = timeMills; 26 | if (timeMills > max) { 27 | resultMills = max; 28 | } 29 | if (timeMills < min) { 30 | resultMills = min; 31 | } 32 | return new DefaultWaitTime(resultMills); 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/wait/ExponentialRetryWait.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.wait; 2 | 3 | import com.poldroc.retry.api.context.RetryWaitContext; 4 | import com.poldroc.retry.api.model.WaitTime; 5 | 6 | /** 7 | * 指数增长的重试等待策略 8 | *

9 | * 1. factor 为重试等待时间的因子 10 | * 2. factor 为 1 时,等待时间为固定值 11 | * 3. factor 大于 1 时,等待时间会逐渐增大 12 | * 4. factor 小于 1 且 大于 0 时,等待时间会逐渐减小 13 | * 14 | * @author Poldroc 15 | * @since 2024/7/11 16 | */ 17 | 18 | public class ExponentialRetryWait extends AbstractRetryWait { 19 | @Override 20 | public WaitTime waitTime(RetryWaitContext retryWaitContext) { 21 | int previousAttempt = retryWaitContext.attempt() - 1; 22 | double pow = Math.pow(retryWaitContext.factor(), previousAttempt); 23 | long result = Math.round(retryWaitContext.value() * pow); 24 | return super.rangeCorrect(result, retryWaitContext.min(), retryWaitContext.max()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/wait/FixedRetryWait.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.wait; 2 | 3 | import com.poldroc.retry.api.context.RetryWaitContext; 4 | import com.poldroc.retry.api.model.WaitTime; 5 | import com.poldroc.retry.common.annotation.ThreadSafe; 6 | 7 | /** 8 | * 固定时间间隔等待 9 | * 10 | * @author Poldroc 11 | * @since 2024/7/11 12 | */ 13 | 14 | @ThreadSafe 15 | public class FixedRetryWait extends AbstractRetryWait { 16 | 17 | @Override 18 | public WaitTime waitTime(RetryWaitContext retryWaitContext) { 19 | return super.rangeCorrect(retryWaitContext.value(), retryWaitContext.min(), retryWaitContext.max()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/wait/IncreaseRetryWait.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.wait; 2 | 3 | import com.poldroc.retry.api.context.RetryWaitContext; 4 | import com.poldroc.retry.api.model.WaitTime; 5 | import com.poldroc.retry.common.annotation.ThreadSafe; 6 | 7 | /** 8 | * 递增重试等待策略 9 | * 10 | * @author Poldroc 11 | * @since 2024/7/11 12 | */ 13 | @ThreadSafe 14 | public class IncreaseRetryWait extends AbstractRetryWait { 15 | @Override 16 | public WaitTime waitTime(RetryWaitContext retryWaitContext) { 17 | int previousAttempt = retryWaitContext.attempt() - 1; 18 | // 结果为重试等待时间的值加上重试次数减一乘以重试等待时间的因子,然后四舍五入 19 | long result = Math.round(retryWaitContext.value() + previousAttempt * retryWaitContext.factor()); 20 | return super.rangeCorrect(result, retryWaitContext.min(), retryWaitContext.max()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /retry-core/src/main/java/com/poldroc/retry/core/support/wait/NoRetryWait.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.core.support.wait; 2 | 3 | import com.poldroc.retry.api.context.RetryWaitContext; 4 | import com.poldroc.retry.api.model.WaitTime; 5 | import com.poldroc.retry.api.support.wait.RetryWait; 6 | import com.poldroc.retry.common.annotation.ThreadSafe; 7 | import com.poldroc.retry.common.support.instance.impl.InstanceFactory; 8 | 9 | /** 10 | * 无时间等待 11 | * 1. 不是很建议使用这种方式 12 | * 2. 一般的异常都有时间性,在一定区间内有问题,那就是有问题。 13 | * @author Poldroc 14 | * @since 2024/7/11 15 | */ 16 | 17 | @ThreadSafe 18 | public class NoRetryWait extends AbstractRetryWait { 19 | 20 | /** 21 | * 获取一个单例示例 22 | * @return 单例示例 23 | */ 24 | public static RetryWait getInstance() { 25 | return InstanceFactory.getInstance().singleton(NoRetryWait.class); 26 | } 27 | 28 | @Override 29 | public WaitTime waitTime(RetryWaitContext retryWaitContext) { 30 | return super.rangeCorrect(0, retryWaitContext.min(), retryWaitContext.max()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /retry-spring/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.poldroc 8 | roc-retry 9 | 1.1 10 | 11 | 12 | retry-spring 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | ${project.groupId} 24 | retry-annotation 25 | 26 | 27 | 28 | org.springframework 29 | spring-aop 30 | 31 | 32 | org.springframework 33 | spring-context 34 | 35 | 36 | 37 | org.aspectj 38 | aspectjweaver 39 | 40 | 41 | org.aspectj 42 | aspectjrt 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /retry-spring/src/main/java/com/poldroc/retry/spring/annotation/EnableRetry.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.spring.annotation; 2 | 3 | import com.poldroc.retry.spring.config.RetryAopConfig; 4 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 5 | import org.springframework.context.annotation.Import; 6 | 7 | import java.lang.annotation.*; 8 | 9 | @Target(ElementType.TYPE) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | @Import(RetryAopConfig.class) 13 | @EnableAspectJAutoProxy 14 | public @interface EnableRetry { 15 | } 16 | -------------------------------------------------------------------------------- /retry-spring/src/main/java/com/poldroc/retry/spring/aop/RetryAop.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.spring.aop; 2 | 3 | import com.poldroc.retry.annotation.handler.method.RetryMethodHandler; 4 | import com.poldroc.retry.annotation.model.RetryAbleBean; 5 | import com.poldroc.retry.common.support.instance.impl.InstanceFactory; 6 | import org.aspectj.lang.ProceedingJoinPoint; 7 | import org.aspectj.lang.Signature; 8 | import org.aspectj.lang.annotation.Around; 9 | import org.aspectj.lang.annotation.Pointcut; 10 | import org.aspectj.lang.reflect.MethodSignature; 11 | import org.springframework.stereotype.Component; 12 | import org.aspectj.lang.annotation.Aspect; 13 | 14 | import java.lang.reflect.Method; 15 | import java.util.Optional; 16 | import java.util.concurrent.Callable; 17 | 18 | /** 19 | * 重试 aop 20 | * 21 | * @author Poldroc 22 | * @since 2024/7/22 23 | */ 24 | 25 | @Aspect 26 | @Component 27 | public class RetryAop { 28 | 29 | 30 | @Pointcut("@annotation(com.poldroc.retry.annotation.annotation.Retry)") 31 | public void myPointcut() { 32 | } 33 | 34 | 35 | @Around("myPointcut()") 36 | public Object around(ProceedingJoinPoint point) throws Throwable { 37 | // 得到方法签名 38 | Signature signature = point.getSignature(); 39 | // 得到方法 40 | Method method = ((MethodSignature) signature).getMethod(); 41 | RetryMethodHandler retryMethodHandler = InstanceFactory.getInstance().singleton(RetryMethodHandler.class); 42 | Object[] args = point.getArgs(); 43 | Optional retryAnnotation = retryMethodHandler.findRetryAnnotation(method, args); 44 | if (!retryAnnotation.isPresent()) { 45 | return point.proceed(); 46 | } 47 | Callable callable = buildCallable(point); 48 | RetryAbleBean retryAbleBean = retryAnnotation.get(); 49 | return retryMethodHandler.retryCall(retryAbleBean, callable); 50 | } 51 | 52 | private Callable buildCallable(ProceedingJoinPoint point) { 53 | return () -> { 54 | try { 55 | return point.proceed(); 56 | } catch (Throwable throwable) { 57 | throw new RuntimeException(throwable); 58 | } 59 | }; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /retry-spring/src/main/java/com/poldroc/retry/spring/config/RetryAopConfig.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.spring.config; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | /** 7 | * 重试配置类 8 | * @author Poldroc 9 | * @since 2024/7/22 10 | */ 11 | 12 | @Configuration 13 | @ComponentScan(basePackages = "com.poldroc.retry.spring") 14 | public class RetryAopConfig { 15 | } 16 | -------------------------------------------------------------------------------- /retry-springboot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.poldroc 8 | roc-retry 9 | 1.1 10 | 11 | 12 | retry-springboot-starter 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | ${project.groupId} 23 | retry-spring 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /retry-springboot-starter/src/main/java/com/poldroc/retry/springboot/starter/config/RocRetryAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.springboot.starter.config; 2 | 3 | import com.poldroc.retry.spring.annotation.EnableRetry; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * 重试自动配置类 9 | * @author Poldroc 10 | * @since 2024/7/22 11 | */ 12 | @EnableRetry 13 | @Configuration 14 | @ConditionalOnClass(EnableRetry.class) 15 | public class RocRetryAutoConfig { 16 | } 17 | -------------------------------------------------------------------------------- /retry-springboot-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.poldroc.retry.springboot.starter.config.RocRetryAutoConfig 2 | -------------------------------------------------------------------------------- /retry-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.poldroc 8 | roc-retry 9 | 1.1 10 | 11 | 12 | retry-test 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | io.github.poldroc 23 | retry-core 24 | 25 | 26 | io.github.poldroc 27 | retry-annotation 28 | 29 | 30 | io.github.poldroc 31 | retry-api 32 | 33 | 34 | 35 | 36 | 37 | 38 | io.github.poldroc 39 | retry-springboot-starter 40 | 41 | 42 | 43 | junit 44 | junit 45 | 46 | 47 | 48 | org.springframework 49 | spring-test 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /retry-test/src/main/java/com/poldroc/retry/test/RocRetryApplication.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test; 2 | 3 | import com.poldroc.retry.spring.annotation.EnableRetry; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.ComponentScan; 7 | 8 | @EnableRetry 9 | @SpringBootApplication 10 | @ComponentScan(basePackages = "com.poldroc.retry.test.service") 11 | public class RocRetryApplication { 12 | public static void main(String[] args) { 13 | SpringApplication.run(RocRetryApplication.class, args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /retry-test/src/main/java/com/poldroc/retry/test/service/SpringRecoverService.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.service; 2 | 3 | 4 | public interface SpringRecoverService { 5 | 6 | /** 7 | * 查询示例代码 8 | * @return 结果 9 | */ 10 | String query(String email); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /retry-test/src/main/java/com/poldroc/retry/test/service/SpringService.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.service; 2 | 3 | 4 | public interface SpringService { 5 | 6 | /** 7 | * 查询示例代码 8 | * @return 结果 9 | */ 10 | String query(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /retry-test/src/main/java/com/poldroc/retry/test/service/impl/SpringRecoverServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.service.impl; 2 | 3 | 4 | import com.poldroc.retry.annotation.annotation.Retry; 5 | 6 | import com.poldroc.retry.annotation.annotation.RetryWait; 7 | import com.poldroc.retry.core.support.wait.FixedRetryWait; 8 | import com.poldroc.retry.test.service.SpringRecoverService; 9 | import com.poldroc.retry.test.support.listens.CustomRetryListen; 10 | import com.poldroc.retry.test.support.recover.MyRecover; 11 | import org.springframework.stereotype.Service; 12 | 13 | @Service 14 | public class SpringRecoverServiceImpl implements SpringRecoverService { 15 | 16 | @Override 17 | @Retry(recover = MyRecover.class,listen = CustomRetryListen.class,waits = @RetryWait(value = 2000, retryWait = FixedRetryWait.class)) 18 | public String query(String email) { 19 | try { 20 | Thread.sleep(1000); 21 | } catch (InterruptedException e) { 22 | e.printStackTrace(); 23 | } 24 | System.out.println("spring service query..." + email); 25 | throw new RuntimeException(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /retry-test/src/main/java/com/poldroc/retry/test/service/impl/SpringServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.service.impl; 2 | 3 | import com.poldroc.retry.annotation.annotation.Retry; 4 | import com.poldroc.retry.test.service.SpringService; 5 | import org.springframework.stereotype.Service; 6 | 7 | /** 8 | * @author Poldroc 9 | * @since 2024/7/22 10 | */ 11 | 12 | @Service 13 | public class SpringServiceImpl implements SpringService { 14 | 15 | @Override 16 | @Retry 17 | public String query() { 18 | 19 | System.out.println("spring service query..."); 20 | throw new RuntimeException(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /retry-test/src/main/java/com/poldroc/retry/test/support/listens/CustomRetryListen.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.support.listens; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.listen.RetryListen; 5 | import com.poldroc.retry.core.support.listen.AbstractRetryListenInit; 6 | 7 | import java.util.LinkedList; 8 | 9 | public class CustomRetryListen extends AbstractRetryListenInit { 10 | @Override 11 | protected void init(LinkedList pipeline, RetryAttempt attempt) { 12 | // 自定义初始化逻辑,比如根据重试次数决定是否添加某个监听器 13 | if (attempt.attempt() == 2) { 14 | pipeline.add(new LogRetryListen()); 15 | } 16 | pipeline.add(new StatRetryListen()); 17 | } 18 | 19 | private class LogRetryListen implements RetryListen { 20 | 21 | @Override 22 | public void listen(RetryAttempt attempt) { 23 | System.out.println("LogRetryListen: " + attempt); 24 | } 25 | } 26 | 27 | private class StatRetryListen implements RetryListen { 28 | @Override 29 | public void listen(RetryAttempt attempt) { 30 | System.out.println("StatRetryListen"); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /retry-test/src/main/java/com/poldroc/retry/test/support/listens/MyListen1.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.support.listens; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.listen.RetryListen; 5 | 6 | public class MyListen1 implements RetryListen { 7 | @Override 8 | public void listen(RetryAttempt attempt) { 9 | System.out.println("MyListen1 listen attempt :" + attempt.attempt()); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /retry-test/src/main/java/com/poldroc/retry/test/support/listens/MyListen2.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.support.listens; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.listen.RetryListen; 5 | 6 | public class MyListen2 implements RetryListen { 7 | @Override 8 | public void listen(RetryAttempt attempt) { 9 | System.out.println("MyListen2 listen time:" + attempt.time().costTimeInMills() + "ms"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /retry-test/src/main/java/com/poldroc/retry/test/support/listens/MyListen3.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.support.listens; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.listen.RetryListen; 5 | 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | 9 | public class MyListen3 implements RetryListen { 10 | @Override 11 | public void listen(RetryAttempt attempt) { 12 | System.out.println("MyListen3 listen history:" + Arrays.toString(attempt.history().toArray())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /retry-test/src/main/java/com/poldroc/retry/test/support/listens/MyListens.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.support.listens; 2 | 3 | import com.poldroc.retry.api.model.RetryAttempt; 4 | import com.poldroc.retry.api.support.listen.RetryListen; 5 | import com.poldroc.retry.core.support.listen.RetryListens; 6 | 7 | public class MyListens implements RetryListen { 8 | 9 | @Override 10 | public void listen(RetryAttempt attempt) { 11 | RetryListens.listens(new MyListen1(), new MyListen2(), new MyListen3()).listen(attempt); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /retry-test/src/main/java/com/poldroc/retry/test/support/recover/MyRecover.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.support.recover; 2 | 3 | 4 | import com.poldroc.retry.api.model.RetryAttempt; 5 | import com.poldroc.retry.api.support.recover.Recover; 6 | /** 7 | * 自定义恢复策略 8 | * @author Poldroc 9 | * @since 2024/7/22 10 | */ 11 | 12 | public class MyRecover implements Recover { 13 | 14 | @Override 15 | public void recover(RetryAttempt retryAttempt) { 16 | Object[] params = retryAttempt.params(); 17 | 18 | String name = params[0].toString(); 19 | // 通知 20 | System.out.println("[Recover] " + name + "查询失败了!"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /retry-test/src/test/java/com/poldroc/retry/test/core/RetryTemplateTest.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.core; 2 | 3 | import com.poldroc.retry.annotation.core.RetryTemplate; 4 | import com.poldroc.retry.test.service.impl.UserServiceImpl; 5 | import org.junit.Test; 6 | 7 | public class RetryTemplateTest { 8 | 9 | 10 | @Test(expected = RuntimeException.class) 11 | public void templateTest() { 12 | UserServiceImpl userService = RetryTemplate.getProxyObject(new UserServiceImpl()); 13 | userService.queryUser(1); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /retry-test/src/test/java/com/poldroc/retry/test/core/RetryerTest.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.core; 2 | 3 | import com.poldroc.retry.core.core.RetryWaiter; 4 | import com.poldroc.retry.core.core.Retryer; 5 | import com.poldroc.retry.core.support.condition.RetryConditions; 6 | import com.poldroc.retry.core.support.listen.RetryListens; 7 | import com.poldroc.retry.core.support.recover.Recovers; 8 | import com.poldroc.retry.core.support.wait.NoRetryWait; 9 | import org.junit.Test; 10 | 11 | import java.util.concurrent.Callable; 12 | 13 | public class RetryerTest { 14 | 15 | /** 16 | * 不会触发重试 17 | */ 18 | @Test 19 | public void commonTest() { 20 | Retryer.newInstance() 21 | .callable(new Callable() { 22 | @Override 23 | public String call() throws Exception { 24 | System.out.println("called..."); 25 | return null; 26 | } 27 | }).retryCall(); 28 | } 29 | 30 | /** 31 | * 默认异常进行重试 32 | */ 33 | @Test(expected = RuntimeException.class) 34 | public void helloTest() { 35 | Retryer.newInstance() 36 | .callable(new Callable() { 37 | @Override 38 | public String call() throws Exception { 39 | System.out.println("called..."); 40 | throw new RuntimeException(); 41 | } 42 | }).retryCall(); 43 | } 44 | 45 | /** 46 | * 默认配置测试 47 | */ 48 | @Test(expected = RuntimeException.class) 49 | public void defaultConfigTest() { 50 | Retryer.newInstance() 51 | .maxAttempt(4) 52 | .listen(RetryListens.noListen()) 53 | .recover(Recovers.noRecover()) 54 | .condition(RetryConditions.hasExceptionCause()) 55 | .retryWaitContext(RetryWaiter.retryWait(NoRetryWait.class).context()) 56 | .callable(new Callable() { 57 | @Override 58 | public String call() throws Exception { 59 | System.out.println("called..."); 60 | throw new RuntimeException(); 61 | } 62 | }).retryCall(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /retry-test/src/test/java/com/poldroc/retry/test/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.service; 2 | 3 | public interface UserService { 4 | 5 | /** 6 | * 查询用户信息 7 | * @param id id 8 | */ 9 | public void queryUser(final long id); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /retry-test/src/test/java/com/poldroc/retry/test/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.service.impl; 2 | 3 | 4 | import com.poldroc.retry.annotation.annotation.Retry; 5 | import com.poldroc.retry.test.service.UserService; 6 | 7 | public class UserServiceImpl implements UserService { 8 | 9 | @Retry(maxAttempt = 5) 10 | @Override 11 | public void queryUser(long id) { 12 | System.out.println("查询用户..."); 13 | throw new RuntimeException(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /retry-test/src/test/java/com/poldroc/retry/test/springboot/RocRetryApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.poldroc.retry.test.springboot; 2 | 3 | import com.poldroc.retry.test.RocRetryApplication; 4 | import com.poldroc.retry.test.service.SpringRecoverService; 5 | import com.poldroc.retry.test.service.SpringService; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.ApplicationContext; 10 | import org.springframework.test.context.ContextConfiguration; 11 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 12 | 13 | @RunWith(SpringJUnit4ClassRunner.class) 14 | @ContextConfiguration(classes = RocRetryApplication.class) 15 | public class RocRetryApplicationTest { 16 | 17 | @Autowired 18 | private SpringService springService; 19 | 20 | @Autowired 21 | private SpringRecoverService springRecoverService; 22 | 23 | @Test(expected = RuntimeException.class) 24 | public void queryTest() { 25 | springService.query(); 26 | } 27 | 28 | @Test(expected = RuntimeException.class) 29 | public void queryRecoverTest() { 30 | String email = "poldroc"; 31 | springRecoverService.query(email); 32 | } 33 | 34 | 35 | } 36 | --------------------------------------------------------------------------------