Repository: alibaba/easy-retry Branch: main Commit: 86b831c436bc Files: 140 Total size: 152.9 KB Directory structure: gitextract_y0h1aiip/ ├── .editorconfig ├── .github/ │ └── workflows/ │ └── maven.yml ├── .gitignore ├── LICENSE ├── README.md ├── config/ │ └── intellij-java-google-style.xml ├── easy-retry-common/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── easyretry/ │ └── common/ │ ├── AbstractResultPredicate.java │ ├── AbstractRetrySyncExecutor.java │ ├── EasyRetryPredicate.java │ ├── Invocation.java │ ├── RetryConfiguration.java │ ├── RetryContainer.java │ ├── RetryContext.java │ ├── RetryExecutor.java │ ├── RetryIdentify.java │ ├── RetryLifecycle.java │ ├── RetrySyncExecutor.java │ ├── SCallable.java │ ├── SimpleMethodInvocation.java │ ├── access/ │ │ ├── RetrySerializerAccess.java │ │ ├── RetryStrategyAccess.java │ │ └── RetryTaskAccess.java │ ├── constant/ │ │ ├── enums/ │ │ │ ├── HandleResultEnum.java │ │ │ ├── RetryTaskStatusEnum.java │ │ │ └── RetryTypeEnum.java │ │ └── package-info.java │ ├── entity/ │ │ └── RetryTask.java │ ├── event/ │ │ ├── RetryEvent.java │ │ ├── RetryEventMulticaster.java │ │ ├── RetryListener.java │ │ ├── before/ │ │ │ ├── AfterSaveBeforeRetryEvent.java │ │ │ ├── BeforeRetryEvent.java │ │ │ └── PrepSaveBeforeRetryEvent.java │ │ └── on/ │ │ ├── FailureOnRetryEvent.java │ │ ├── OnRetryEvent.java │ │ ├── StopOnRetryEvent.java │ │ └── SuccessOnRetryEvent.java │ ├── filter/ │ │ ├── AbstractRetryFilter.java │ │ ├── RetryFilter.java │ │ ├── RetryFilterDiscover.java │ │ ├── RetryFilterInvocation.java │ │ ├── RetryFilterInvocationHandler.java │ │ ├── RetryFilterRegister.java │ │ ├── RetryFilterRegisterHandler.java │ │ └── RetryFilterResponse.java │ ├── processor/ │ │ ├── AsyncPersistenceProcessor.java │ │ ├── RetryProcessor.java │ │ └── SyncProcessor.java │ ├── resolve/ │ │ └── ExecutorSolver.java │ ├── retryer/ │ │ ├── Retryer.java │ │ └── RetryerInfo.java │ ├── serializer/ │ │ ├── ArgDeSerializerInfo.java │ │ ├── ArgSerializerInfo.java │ │ ├── EasyRetrySerializer.java │ │ ├── ResultPredicateSerializer.java │ │ └── RetryArgSerializer.java │ └── strategy/ │ ├── RetryStrategy.java │ ├── StopStrategy.java │ └── WaitStrategy.java ├── easy-retry-core/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── easyretry/ │ │ └── core/ │ │ ├── DegradeAbleRetryExecutor.java │ │ ├── PersistenceRetryExecutor.java │ │ ├── PersistenceRetryer.java │ │ ├── PersistenceRetryerBuilder.java │ │ ├── RetryerBuilder.java │ │ ├── SyncRetryer.java │ │ ├── SyncRetryerBuilder.java │ │ ├── access/ │ │ │ ├── DefaultRetrySerializerAccess.java │ │ │ └── MemoryRetryTaskAccess.java │ │ ├── container/ │ │ │ └── SimpleRetryContainer.java │ │ ├── context/ │ │ │ └── MaxAttemptsPersistenceRetryContext.java │ │ ├── degrade/ │ │ │ └── EasyRetryDegradeHelper.java │ │ ├── event/ │ │ │ └── SimpleRetryEventMulticaster.java │ │ ├── filter/ │ │ │ ├── DefaultRetryFilterInvocationHandler.java │ │ │ ├── DefaultRetryFilterRegisterHandler.java │ │ │ ├── IdentifyRetryFilter.java │ │ │ ├── MethodExcuteRetryFilter.java │ │ │ ├── NOOPRetryFilter.java │ │ │ ├── SPIRetryFilterDiscover.java │ │ │ └── SimpleRetryFilterRegister.java │ │ ├── process/ │ │ │ ├── async/ │ │ │ │ ├── AbstractAsyncPersistenceProcessor.java │ │ │ │ ├── before/ │ │ │ │ │ ├── AbstractAsyncPersistenceBeforeRetryProcessor.java │ │ │ │ │ ├── ExceptionPersistenceAsyncBeforeRetryProcessor.java │ │ │ │ │ └── ResultAsynPersistenceBeforeRetryProcessor.java │ │ │ │ └── on/ │ │ │ │ ├── AbstractAsyncPersistenceOnRetryProcessor.java │ │ │ │ ├── ExceptionPersistenceAsynOnRetryProcessor.java │ │ │ │ └── ResultAsynPersistenceOnRetryProcessor.java │ │ │ └── package-info.java │ │ ├── serializer/ │ │ │ ├── FastJsonRetryArgSerializer.java │ │ │ ├── HessianResultPredicateSerializer.java │ │ │ └── HessianRetryArgSerializer.java │ │ ├── strategy/ │ │ │ └── DefaultRetryStrategy.java │ │ └── utils/ │ │ ├── HessianSerializerUtils.java │ │ ├── LogUtils.java │ │ └── PrintUtils.java │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── easyretry/ │ └── core/ │ └── utils/ │ └── TestClass.java ├── easy-retry-extensions/ │ ├── easy-retry-guava-extension/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── easyretry/ │ │ └── extension/ │ │ └── guava/ │ │ ├── GuavaRetrySyncExecutor.java │ │ └── package-info.java │ ├── easy-retry-mybatis-extension/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── easyretry/ │ │ │ │ └── extension/ │ │ │ │ └── mybatis/ │ │ │ │ ├── access/ │ │ │ │ │ └── MybatisRetryTaskAccess.java │ │ │ │ ├── common/ │ │ │ │ │ └── utils/ │ │ │ │ │ └── HostUtils.java │ │ │ │ ├── dao/ │ │ │ │ │ ├── BaseDAOSupport.java │ │ │ │ │ ├── RetryTaskDAO.java │ │ │ │ │ └── RetryTaskDAOImpl.java │ │ │ │ ├── po/ │ │ │ │ │ └── RetryTaskPO.java │ │ │ │ └── query/ │ │ │ │ └── RetryTaskQuery.java │ │ │ └── resources/ │ │ │ └── dal/ │ │ │ └── easyretry/ │ │ │ ├── easy-mybatis-config.xml │ │ │ └── mapper/ │ │ │ └── easy-retry-task-mapper.xml │ │ └── test/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── easyretry/ │ │ │ └── extension/ │ │ │ └── mybatis/ │ │ │ ├── DbConfig.java │ │ │ ├── MyBatisConfig.java │ │ │ ├── access/ │ │ │ │ └── MybatisRetryTaskAccessTest.java │ │ │ ├── common/ │ │ │ │ └── utils/ │ │ │ │ └── HostUtilsTest.java │ │ │ └── dao/ │ │ │ └── RetryTaskDAOImplTest.java │ │ └── resources/ │ │ ├── logback.xml │ │ └── task.sql │ ├── easy-retry-spring-extension/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── easyretry/ │ │ └── extension/ │ │ └── spring/ │ │ ├── RetryListenerInitialize.java │ │ ├── SPELParamPredicate.java │ │ ├── SPELResultPredicate.java │ │ ├── SpringEventApplicationListener.java │ │ ├── SpringRetryFilterDiscover.java │ │ └── aop/ │ │ ├── EasyRetryable.java │ │ └── RetryInterceptor.java │ └── pom.xml ├── easy-retry-starters/ │ ├── easy-retry-memory-starter/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── easyretry/ │ │ │ └── memory/ │ │ │ ├── MemoryAutoConfiguration.java │ │ │ └── config/ │ │ │ └── EasyRetryMemoryCompatibleProperties.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── additional-spring-configuration-metadata.json │ │ └── spring.factories │ ├── easy-retry-mybatis-starter/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── easyretry/ │ │ │ └── mybatis/ │ │ │ ├── MybatisAutoConfiguration.java │ │ │ └── conifg/ │ │ │ └── EasyRetryMybatisProperties.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── additional-spring-configuration-metadata.json │ │ └── spring.factories │ ├── easy-retry-starter-common/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── easyretry/ │ │ └── starter/ │ │ └── common/ │ │ └── CommonAutoConfiguration.java │ └── pom.xml └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 [*.java] indent_style = tab indent_size = 4 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true [*.{json, yml, xml}] indent_style = tab indent_size = 4 [*.md] insert_final_newline = false trim_trailing_whitespace = false [*.properties] ij_properties_align_group_field_declarations = false ij_properties_keep_blank_lines = false ij_properties_key_value_delimiter = equals ij_properties_spaces_around_key_value_delimiter = false ================================================ FILE: .github/workflows/maven.yml ================================================ # This workflow will build a Java project with Maven # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven name: Java CI with Maven on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up JDK 1.8 uses: actions/setup-java@v1 with: java-version: 1.8 - name: Build with Maven run: mvn -B package --file pom.xml ================================================ FILE: .gitignore ================================================ # Compiled class file *.class *.classpath *.factorypath # Log file *.log # BlueJ files *.ctxt # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.ear *.zip *.tar.gz *.rar # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* # IDE Files # *.iml .idea .idea/ .project .settings target .DS_Store # temp ignore *.cache *.diff *.patch *.tmp # Maven ignore .flattened-pom.xml ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 Alibaba Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Easy-Retry 一种存储介质可扩展的持久化重试方案 ![img](img/readme/arch.jpg) ### Getting started #### Memory Retry 1. 增加pom依赖 ```xml com.alibaba easy-retry-memory-starter ${last-version} ``` 2. 在application.properties增加配置 `spring.easyretry.memory.enabled = true` 3. 在需要重试的方法上增加`@EasyRetryable`注解 ```java public class MemoryUserService { @EasyRetryable public User getUserById(Long userId){ return new User(); } } ``` #### Mybatis Retry 1. 增加pom依赖 ```xml com.alibaba easy-retry-mybatis-starter ${last-version} ``` 2. 在application.properties增加配置 `spring.easyretry.mybatis.enabled = true` 3. 声明`javax.sql.DataSource`的`Bean`实例,参考下面例子(以`druid`连接池为例) ``` @Bean(name = "easyRetryMybatisDataSource", initMethod = "init", destroyMethod = "close") public DataSource easyRetryMybatisDataSource() { DruidDataSource tds = new DruidDataSource(); tds.setUrl(""); tds.setUsername(""); tds.setPassword(""); ... return tds; } ``` 4. 新增持久化表 ``` CREATE TABLE `easy_retry_task` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `gmt_create` datetime NOT NULL COMMENT '创建时间', `gmt_modified` datetime NOT NULL COMMENT '修改时间', `sharding` varchar(64) DEFAULT NULL COMMENT '数据库分片字段', `biz_id` varchar(64) DEFAULT NULL COMMENT '业务id', `executor_name` varchar(512) NOT NULL COMMENT '执行名称', `executor_method_name` varchar(512) NOT NULL COMMENT '执行方法名称', `retry_status` tinyint(4) NOT NULL COMMENT '重试状态', `args_str` varchar(7168) DEFAULT NULL COMMENT '执行方法参数', `ext_attrs` varchar(3000) DEFAULT NULL COMMENT '扩展字段', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COMMENT='easy_retry_task' ; ``` 5. 在需要重试的方法上增加@EasyRetryable注解 ```java public class MybatisUserService { @EasyRetryable public User getUserById(Long userId){ return new User(); } } ``` ### Built With • JDK1.8 • Spring Framework5+ • Spring Boot2.4+ • Maven3.0 ================================================ FILE: config/intellij-java-google-style.xml ================================================ ================================================ FILE: easy-retry-common/pom.xml ================================================ 4.0.0 com.alibaba easy-retry ${revision} ../pom.xml easy-retry-common easy-retry-common easy-retry-common org.projectlombok lombok org.apache.commons commons-lang3 com.google.guava guava org.slf4j slf4j-api ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/AbstractResultPredicate.java ================================================ package com.alibaba.easyretry.common; /** * @author Created by wuhao on 2021/3/26. */ public abstract class AbstractResultPredicate implements EasyRetryPredicate { @Override public abstract Boolean apply(T result); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/AbstractRetrySyncExecutor.java ================================================ package com.alibaba.easyretry.common; import com.alibaba.easyretry.common.retryer.RetryerInfo; /** * @author zhangchi20 * Created on 2023-07-17 */ public abstract class AbstractRetrySyncExecutor implements RetrySyncExecutor { private RetryerInfo retryerInfo; @Override public void setRetryerInfo(RetryerInfo retryerInfo) { this.retryerInfo = retryerInfo; } public RetryerInfo getRetryerInfo() { return retryerInfo; } @Override public abstract V call(SCallable callable) throws Throwable; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/EasyRetryPredicate.java ================================================ package com.alibaba.easyretry.common; import java.io.Serializable; /** * @author Created by wuhao on 2021/3/18. */ public interface EasyRetryPredicate extends Serializable { R apply(T result); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/Invocation.java ================================================ package com.alibaba.easyretry.common; /** * @author Created by wuhao on 2021/3/29. */ public interface Invocation { Object invoke() throws Throwable; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/RetryConfiguration.java ================================================ package com.alibaba.easyretry.common; import com.alibaba.easyretry.common.access.RetrySerializerAccess; import com.alibaba.easyretry.common.access.RetryStrategyAccess; import com.alibaba.easyretry.common.access.RetryTaskAccess; import com.alibaba.easyretry.common.event.RetryEventMulticaster; import com.alibaba.easyretry.common.resolve.ExecutorSolver; import com.alibaba.easyretry.common.serializer.ResultPredicateSerializer; /** * @author Created by wuhao on 2020/11/5. */ public interface RetryConfiguration { RetryTaskAccess getRetryTaskAccess(); RetrySerializerAccess getRetrySerializerAccess(); RetryStrategyAccess getRetryStrategyAccess(); ExecutorSolver getExecutorSolver(); ResultPredicateSerializer getResultPredicateSerializer(); Integer getMaxRetryTimes(); RetryEventMulticaster getRetryEventMulticaster(); default AbstractRetrySyncExecutor getRetrySyncExecutor() { return null; } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/RetryContainer.java ================================================ package com.alibaba.easyretry.common; /** * @author Created by wuhao on 2020/11/5. */ public interface RetryContainer extends RetryLifecycle { } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/RetryContext.java ================================================ package com.alibaba.easyretry.common; public interface RetryContext extends RetryLifecycle { void setAttribute(String key, String value); String getAttribute(String key); /** * 获取唯一标识 */ String getId(); Invocation getInvocation(); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/RetryExecutor.java ================================================ package com.alibaba.easyretry.common; import com.alibaba.easyretry.common.constant.enums.HandleResultEnum; /** * @author Created by wuhao on 2021/3/2. */ public interface RetryExecutor { HandleResultEnum doExecute(RetryContext context); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/RetryIdentify.java ================================================ package com.alibaba.easyretry.common; import java.util.Objects; /** * @author Created by wuhao on 2021/2/20. */ public class RetryIdentify { private static final ThreadLocal RETRY_CONTEXT_THREAD_LOCAL = new ThreadLocal<>(); private static final String RETRY_FLAG = "RETRY_FLAG"; public static void start() { RETRY_CONTEXT_THREAD_LOCAL.set(RETRY_FLAG); } public static void stop() { RETRY_CONTEXT_THREAD_LOCAL.set(null); } public static boolean isOnRetry() { return Objects.nonNull(RETRY_CONTEXT_THREAD_LOCAL.get()); } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/RetryLifecycle.java ================================================ package com.alibaba.easyretry.common; /** * @author Created by wuhao on 2020/11/2. */ public interface RetryLifecycle { void start(); void stop(); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/RetrySyncExecutor.java ================================================ package com.alibaba.easyretry.common; import com.alibaba.easyretry.common.retryer.RetryerInfo; /** * @author zhangchi20 * Created on 2023-07-17 */ public interface RetrySyncExecutor { V call(SCallable callable) throws Throwable; void setRetryerInfo(RetryerInfo retryerInfo); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/SCallable.java ================================================ package com.alibaba.easyretry.common; import java.io.Serializable; /** * @author Created by wuhao on 2020/11/5. */ @FunctionalInterface public interface SCallable extends Serializable { V call() throws Throwable; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/SimpleMethodInvocation.java ================================================ package com.alibaba.easyretry.common; import java.lang.reflect.Method; import java.util.Arrays; import lombok.AllArgsConstructor; import lombok.Setter; /** * @author Created by wuhao on 2021/3/29. */ @AllArgsConstructor public class SimpleMethodInvocation implements Invocation { @Setter private Object executor; @Setter private Method method; @Setter private Object[] args; @Override public Object invoke() throws Throwable { return method.invoke(executor, args); } @Override public String toString() { return "[Invocation] executor is " + executor.getClass().getName() + " method is " + method .getName() + " args is " + Arrays.toString(args); } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/access/RetrySerializerAccess.java ================================================ package com.alibaba.easyretry.common.access; import com.alibaba.easyretry.common.serializer.RetryArgSerializer; /** * 重试信息序列化器获取 如果方法上有指定序列化器,则使用getRetrySerializer 否则使用全局序列化器getCurrentGlobalRetrySerializer * * @author Created by wuhao on 2020/11/6. */ public interface RetrySerializerAccess { /** * 获取全局序列化器 */ RetryArgSerializer getCurrentGlobalRetrySerializer(); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/access/RetryStrategyAccess.java ================================================ package com.alibaba.easyretry.common.access; import com.alibaba.easyretry.common.strategy.StopStrategy; import com.alibaba.easyretry.common.strategy.WaitStrategy; /** * 重试策略获取 如果方法上有指定则用指定Strategy 否则使用Global * * @author Created by wuhao on 2020/11/6. */ public interface RetryStrategyAccess { /** * 获取全局重试任务停止策略 */ StopStrategy getCurrentGlobalStopStrategy(); /** * 获取全局等待策略 */ WaitStrategy getCurrentGlobalWaitStrategy(); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/access/RetryTaskAccess.java ================================================ package com.alibaba.easyretry.common.access; import java.util.List; import com.alibaba.easyretry.common.entity.RetryTask; /** * 重试任务获取器 */ public interface RetryTaskAccess { /** * 保存重试任务 */ boolean saveRetryTask(RetryTask retryTask); /** * 更改重试任务为处理中 */ boolean handlingRetryTask(RetryTask retryTask); /** * 完结重试任务 */ boolean finishRetryTask(RetryTask retryTask); /** * 停止重试任务 */ boolean stopRetryTask(RetryTask retryTask); /** * 批量查询重试任务 */ List listAvailableTasks(Long lastId); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/constant/enums/HandleResultEnum.java ================================================ package com.alibaba.easyretry.common.constant.enums; /** * 重试任务重试结果 * * @author Created by wuhao on 2020/11/1. */ public enum HandleResultEnum { /** * 处理成功 */ SUCCESS, /** * 处理失败 */ FAILURE, /** * 等待策略返回该case还在等待队列中 */ WAITING, /** * 根据停止策略该笔case停止重试 */ STOP, ERROR; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/constant/enums/RetryTaskStatusEnum.java ================================================ package com.alibaba.easyretry.common.constant.enums; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; import lombok.Getter; /** * 重试任务状态 * * @author Created by wuhao on 2020/10/31. */ public enum RetryTaskStatusEnum { /** * 初始化状态 */ INIT(0, "初始化"), /** * 任务处理中 */ HANDLING(1, "处理中"), /** * 任务处理异常 */ ERROR(2, "异常"), /** * 任务完结 */ FINISH(3, "完结"); private static final Map MAP = Stream.of(values()) .collect(Collectors.toMap(RetryTaskStatusEnum::getCode, (value) -> value)); @Getter private int code; @Getter private String desc; RetryTaskStatusEnum(int code, String desc) { this.code = code; this.desc = desc; } public static RetryTaskStatusEnum fromCode(int code) { return MAP.get(code); } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/constant/enums/RetryTypeEnum.java ================================================ package com.alibaba.easyretry.common.constant.enums; /** * 重试种类 * * @author Created by zhangchi on 2023-07-12 */ public enum RetryTypeEnum { /** * 同步 */ SYNC, /** * 异步 */ ASYNC, ; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/constant/package-info.java ================================================ /** * @author Created by wuhao on 2020/11/6. */ package com.alibaba.easyretry.common.constant; ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/entity/RetryTask.java ================================================ package com.alibaba.easyretry.common.entity; import java.util.Date; import java.util.Map; import com.alibaba.easyretry.common.constant.enums.RetryTaskStatusEnum; import lombok.Data; /** * 重试任务实体 * * @author wuhao */ @Data public class RetryTask { /** * 主键id */ private Long id; /** * 业务信息 */ private String bizId; /** * 执行者名称 */ private String executorName; /** * 执行者方法 */ private String executorMethodName; /** * 当重试失败时候执行的方法 */ private String onFailureMethod; /** * 重试任务状态 */ private RetryTaskStatusEnum status; /** * 任务上的扩展字段 */ private Map extAttrs; /** * 重试执行者方法参数 */ private String argsStr; private Date gmtCreate; private Date gmtModified; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/event/RetryEvent.java ================================================ package com.alibaba.easyretry.common.event; /** * @author Created by wuhao on 2021/3/25. */ public interface RetryEvent { String getName(); boolean isOnRetry(); void setAttribute(String key, String vule); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/event/RetryEventMulticaster.java ================================================ package com.alibaba.easyretry.common.event; /** * @author Created by wuhao on 2021/3/26. */ public interface RetryEventMulticaster { void register(RetryListener listener); void multicast(RetryEvent retryEvent); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/event/RetryListener.java ================================================ package com.alibaba.easyretry.common.event; /** * @author Created by wuhao on 2021/3/25. */ public interface RetryListener { void onRetryEvent(T event); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/event/before/AfterSaveBeforeRetryEvent.java ================================================ package com.alibaba.easyretry.common.event.before; import com.alibaba.easyretry.common.entity.RetryTask; /** * @author Created by wuhao on 2021/3/25. */ public class AfterSaveBeforeRetryEvent extends BeforeRetryEvent { public AfterSaveBeforeRetryEvent(RetryTask retryTask) { super(retryTask); } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/event/before/BeforeRetryEvent.java ================================================ package com.alibaba.easyretry.common.event.before; import java.util.Map; import java.util.Objects; import com.alibaba.easyretry.common.entity.RetryTask; import com.alibaba.easyretry.common.event.RetryEvent; import com.google.common.collect.Maps; /** * @author Created by wuhao on 2021/3/25. */ public abstract class BeforeRetryEvent implements RetryEvent { private RetryTask retryTask; public BeforeRetryEvent(RetryTask retryTask) { this.retryTask = retryTask; } @Override public boolean isOnRetry() { return false; } @Override public void setAttribute(String key, String value) { Map extAttrs = retryTask.getExtAttrs(); if (Objects.isNull(extAttrs)) { extAttrs = Maps.newHashMap(); } extAttrs.put(key, value); } @Override public String getName() { return this.getClass().getSimpleName(); } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/event/before/PrepSaveBeforeRetryEvent.java ================================================ package com.alibaba.easyretry.common.event.before; import com.alibaba.easyretry.common.entity.RetryTask; /** * @author Created by wuhao on 2021/3/25. */ public class PrepSaveBeforeRetryEvent extends BeforeRetryEvent { public PrepSaveBeforeRetryEvent(RetryTask retryTask) { super(retryTask); } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/event/on/FailureOnRetryEvent.java ================================================ package com.alibaba.easyretry.common.event.on; import com.alibaba.easyretry.common.RetryContext; /** * @author Created by wuhao on 2021/3/25. */ public class FailureOnRetryEvent extends OnRetryEvent { public FailureOnRetryEvent(RetryContext retryContext) { super(retryContext); } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/event/on/OnRetryEvent.java ================================================ package com.alibaba.easyretry.common.event.on; import com.alibaba.easyretry.common.RetryContext; import com.alibaba.easyretry.common.event.RetryEvent; /** * @author Created by wuhao on 2021/3/25. */ public abstract class OnRetryEvent implements RetryEvent { final private RetryContext retryContext; public OnRetryEvent(RetryContext retryContext) { this.retryContext = retryContext; } @Override public void setAttribute(String key, String value) { retryContext.setAttribute(key, value); } public String getAttribute(String key) { return retryContext.getAttribute(key); } @Override public boolean isOnRetry() { return true; } @Override public String getName() { return this.getClass().getSimpleName(); } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/event/on/StopOnRetryEvent.java ================================================ package com.alibaba.easyretry.common.event.on; import com.alibaba.easyretry.common.RetryContext; /** * @author Created by wuhao on 2021/3/25. */ public class StopOnRetryEvent extends OnRetryEvent { public StopOnRetryEvent(RetryContext retryContext) { super(retryContext); } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/event/on/SuccessOnRetryEvent.java ================================================ package com.alibaba.easyretry.common.event.on; import com.alibaba.easyretry.common.RetryContext; /** * @author Created by wuhao on 2021/3/25. */ public class SuccessOnRetryEvent extends OnRetryEvent { public SuccessOnRetryEvent(RetryContext retryContext) { super(retryContext); } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/filter/AbstractRetryFilter.java ================================================ package com.alibaba.easyretry.common.filter; /** * @author Created by wuhao on 2021/3/22. */ public abstract class AbstractRetryFilter implements RetryFilter { protected RetryFilter next; @Override public void setNext(RetryFilter next) { this.next = next; } } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/filter/RetryFilter.java ================================================ package com.alibaba.easyretry.common.filter; import com.alibaba.easyretry.common.RetryContext; /** * @author Created by wuhao on 2021/3/22. */ public interface RetryFilter { RetryFilterResponse doFilter(RetryContext retryContext) throws Throwable; void setNext(RetryFilter next); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/filter/RetryFilterDiscover.java ================================================ package com.alibaba.easyretry.common.filter; import java.util.List; /** * @author Created by wuhao on 2021/4/9. */ public interface RetryFilterDiscover { List discoverAll(); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/filter/RetryFilterInvocation.java ================================================ package com.alibaba.easyretry.common.filter; import com.alibaba.easyretry.common.RetryContext; /** * @author Created by wuhao on 2021/4/10. */ public interface RetryFilterInvocation { RetryFilterResponse invoke(RetryContext retryContext) throws Throwable; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/filter/RetryFilterInvocationHandler.java ================================================ package com.alibaba.easyretry.common.filter; /** * @author Created by wuhao on 2021/3/19. */ public interface RetryFilterInvocationHandler { void handle(); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/filter/RetryFilterRegister.java ================================================ package com.alibaba.easyretry.common.filter; import java.util.List; /** * @author Created by wuhao on 2021/3/22. */ public interface RetryFilterRegister { void register(RetryFilter retryFilter); List export(); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/filter/RetryFilterRegisterHandler.java ================================================ package com.alibaba.easyretry.common.filter; /** * @author Created by wuhao on 2021/4/9. */ public interface RetryFilterRegisterHandler { void handle(); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/filter/RetryFilterResponse.java ================================================ package com.alibaba.easyretry.common.filter; import lombok.Data; /** * @author Created by wuhao on 2021/3/22. */ @Data public class RetryFilterResponse { private Object response; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/processor/AsyncPersistenceProcessor.java ================================================ package com.alibaba.easyretry.common.processor; /** * @author Created by wuhao on 2021/3/19. */ public interface AsyncPersistenceProcessor extends RetryProcessor { @Override void process(); boolean needRetry(); R getResult() throws Throwable; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/processor/RetryProcessor.java ================================================ package com.alibaba.easyretry.common.processor; /** * @author Created by wuhao on 2021/3/19. */ public interface RetryProcessor { void process(); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/processor/SyncProcessor.java ================================================ package com.alibaba.easyretry.common.processor; /** * @author Created by zhangchi on 2023-07-12 */ public interface SyncProcessor extends RetryProcessor { R getResult() throws Throwable; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/resolve/ExecutorSolver.java ================================================ package com.alibaba.easyretry.common.resolve; /** * @author Created by wuhao on 2020/11/8. */ public interface ExecutorSolver { Object resolver(String executorName); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/retryer/Retryer.java ================================================ package com.alibaba.easyretry.common.retryer; import com.alibaba.easyretry.common.SCallable; /** * @author Created by wuhao on 2020/11/5. */ public interface Retryer { V call(SCallable callable) throws Throwable; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/retryer/RetryerInfo.java ================================================ package com.alibaba.easyretry.common.retryer; import com.alibaba.easyretry.common.AbstractResultPredicate; import com.alibaba.easyretry.common.RetryConfiguration; import lombok.Data; import lombok.experimental.Accessors; /** * @author Created by wuhao on 2021/3/19. */ @Data @Accessors(chain = true) public class RetryerInfo { /** * 执行者名称 */ private String executorName; /** * 执行者方法 */ private String executorMethodName; private String onFailureMethod; /** * 业务id,外部可以自定义存储一些信息 */ private String bizId; private Object[] args; private Class onException; private RetryConfiguration retryConfiguration; private String namespace; private boolean reThrowException; private AbstractResultPredicate resultPredicate; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/serializer/ArgDeSerializerInfo.java ================================================ package com.alibaba.easyretry.common.serializer; import lombok.Data; /** * @author Created by wuhao on 2020/11/1. */ @Data public class ArgDeSerializerInfo { private String argsStr; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/serializer/ArgSerializerInfo.java ================================================ package com.alibaba.easyretry.common.serializer; import lombok.Data; /** * @author Created by wuhao on 2020/11/1. */ @Data public class ArgSerializerInfo { /** * 执行者名称 */ private String executorName; private String executorClassName; /** * 执行者方法 */ private String executorMethodName; private Object[] args; } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/serializer/EasyRetrySerializer.java ================================================ package com.alibaba.easyretry.common.serializer; /** * @author Created by wuhao on 2021/3/18. */ public interface EasyRetrySerializer { String serialize(T serializeInfo); T deSerialize(String infoStr); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/serializer/ResultPredicateSerializer.java ================================================ package com.alibaba.easyretry.common.serializer; import com.alibaba.easyretry.common.AbstractResultPredicate; /** * @author Created by wuhao on 2021/3/18. */ public interface ResultPredicateSerializer extends EasyRetrySerializer { } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/serializer/RetryArgSerializer.java ================================================ package com.alibaba.easyretry.common.serializer; /** * Retry 序列化器 */ public interface RetryArgSerializer extends EasyRetrySerializer { } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/strategy/RetryStrategy.java ================================================ package com.alibaba.easyretry.common.strategy; import com.alibaba.easyretry.common.RetryContext; /** * @author Created by wuhao on 2020/11/2. */ public interface RetryStrategy { void clear(RetryContext context); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/strategy/StopStrategy.java ================================================ package com.alibaba.easyretry.common.strategy; import com.alibaba.easyretry.common.RetryContext; /** * @author Created by wuhao on 2020/11/1. */ public interface StopStrategy extends RetryStrategy { boolean shouldStop(RetryContext context); } ================================================ FILE: easy-retry-common/src/main/java/com/alibaba/easyretry/common/strategy/WaitStrategy.java ================================================ package com.alibaba.easyretry.common.strategy; import com.alibaba.easyretry.common.RetryContext; /** * @author Created by wuhao on 2020/11/1. */ public interface WaitStrategy extends RetryStrategy { boolean shouldWait(RetryContext context); void backOff(RetryContext context); } ================================================ FILE: easy-retry-core/pom.xml ================================================ 4.0.0 com.alibaba easy-retry ${revision} ../pom.xml easy-retry-core easy-retry-core easy-retry-core ${project.groupId} easy-retry-common org.apache.commons commons-lang3 com.google.guava guava com.caucho hessian org.slf4j slf4j-api com.alibaba fastjson org.apache.commons commons-collections4 ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/DegradeAbleRetryExecutor.java ================================================ package com.alibaba.easyretry.core; import com.alibaba.easyretry.common.RetryContext; import com.alibaba.easyretry.common.RetryExecutor; import com.alibaba.easyretry.common.constant.enums.HandleResultEnum; import com.alibaba.easyretry.core.degrade.EasyRetryDegradeHelper; import lombok.Setter; import lombok.extern.slf4j.Slf4j; /** * @author Created by gejinfeng on 2021/4/29. */ @Slf4j public class DegradeAbleRetryExecutor implements RetryExecutor { @Setter private RetryExecutor retryExecutor; @Setter private EasyRetryDegradeHelper easyRetryDegradeHelper; @Override public HandleResultEnum doExecute(RetryContext context) { if (easyRetryDegradeHelper.degrade(context)) { return HandleResultEnum.STOP; } return retryExecutor.doExecute(context); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/PersistenceRetryExecutor.java ================================================ package com.alibaba.easyretry.core; import java.lang.reflect.InvocationTargetException; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.RetryContext; import com.alibaba.easyretry.common.RetryExecutor; import com.alibaba.easyretry.common.access.RetryTaskAccess; import com.alibaba.easyretry.common.constant.enums.HandleResultEnum; import com.alibaba.easyretry.common.entity.RetryTask; import com.alibaba.easyretry.common.event.RetryEvent; import com.alibaba.easyretry.common.event.on.FailureOnRetryEvent; import com.alibaba.easyretry.common.event.on.StopOnRetryEvent; import com.alibaba.easyretry.common.event.on.SuccessOnRetryEvent; import com.alibaba.easyretry.common.filter.RetryFilterInvocation; import com.alibaba.easyretry.common.filter.RetryFilterResponse; import com.alibaba.easyretry.core.context.MaxAttemptsPersistenceRetryContext; import com.alibaba.easyretry.core.process.async.on.AbstractAsyncPersistenceOnRetryProcessor; import com.alibaba.easyretry.core.process.async.on.ExceptionPersistenceAsynOnRetryProcessor; import com.alibaba.easyretry.core.process.async.on.ResultAsynPersistenceOnRetryProcessor; import com.alibaba.easyretry.core.utils.LogUtils; import com.alibaba.easyretry.core.utils.PrintUtils; import lombok.Setter; import lombok.extern.slf4j.Slf4j; /** * @author Created by wuhao on 2021/3/2. */ @Slf4j public class PersistenceRetryExecutor implements RetryExecutor { @Setter private RetryConfiguration retryConfiguration; @Setter private RetryFilterInvocation retryFilterInvocation; @Override public HandleResultEnum doExecute(RetryContext context) { try { PrintUtils.monitorInfo("begin deal", context); return handle(context); } catch (Throwable e) { log.error("Retry invoke failed", e); return HandleResultEnum.ERROR; } } private HandleResultEnum handle(RetryContext context) { MaxAttemptsPersistenceRetryContext maxAttemptsPersistenceRetryContext = (MaxAttemptsPersistenceRetryContext)context; if (maxAttemptsPersistenceRetryContext.getWaitStrategy().shouldWait(context)) { PrintUtils.monitorInfo("shouldWait", context); return HandleResultEnum.WAITING; } PrintUtils.monitorInfo("handlingRetryTask", context); retryConfiguration.getRetryTaskAccess() .handlingRetryTask(maxAttemptsPersistenceRetryContext.getRetryTask()); AbstractAsyncPersistenceOnRetryProcessor abstractAsynPersistenceOnRetryProcessor; try { PrintUtils.monitorInfo("beigin excuteMethod", context); RetryFilterResponse retryFilterResponse = retryFilterInvocation.invoke(context); abstractAsynPersistenceOnRetryProcessor = new ResultAsynPersistenceOnRetryProcessor<>( retryFilterResponse.getResponse(), maxAttemptsPersistenceRetryContext); PrintUtils.monitorInfo("excuteMethod success ", context); } catch (Throwable t) { if (t instanceof InvocationTargetException) { t = t.getCause(); } log.error("excuteMethod failed task arg is {} task id is {}", context.getInvocation(), context.getId(), t); abstractAsynPersistenceOnRetryProcessor = new ExceptionPersistenceAsynOnRetryProcessor<>( t, maxAttemptsPersistenceRetryContext); } abstractAsynPersistenceOnRetryProcessor.process(); HandleResultEnum handleResult = abstractAsynPersistenceOnRetryProcessor.getRetryResult(); PrintUtils.monitorInfo("handleResult ", context, "handleResult is " + handleResult); RetryEvent onRetryEvent = null; switch (handleResult) { case SUCCESS: finish(context); onRetryEvent = new SuccessOnRetryEvent(context); break; case STOP: stop(context); onRetryEvent = new StopOnRetryEvent(context); break; case FAILURE: onRetryEvent = new FailureOnRetryEvent(context); } retryConfiguration.getRetryEventMulticaster().multicast(onRetryEvent); return handleResult; } private void finish(RetryContext context) { MaxAttemptsPersistenceRetryContext maxAttemptsPersistenceRetryContext = (MaxAttemptsPersistenceRetryContext)context; RetryTask retryTask = maxAttemptsPersistenceRetryContext.getRetryTask(); try { RetryTaskAccess retryTaskAccess = retryConfiguration.getRetryTaskAccess(); retryTaskAccess.finishRetryTask(retryTask); context.stop(); } catch (Throwable t) { LogUtils.CONSISTENCY_LOGGER .error("finishRetryTask error " + context.getInvocation() + " please check", t); } } private void stop(RetryContext context) { try { MaxAttemptsPersistenceRetryContext maxAttemptsPersistenceRetryContext = (MaxAttemptsPersistenceRetryContext)context; RetryTaskAccess retryTaskAccess = retryConfiguration.getRetryTaskAccess(); retryTaskAccess.stopRetryTask(maxAttemptsPersistenceRetryContext.getRetryTask()); //executeOnFailureMethod(context); context.stop(); } catch (Throwable t) { LogUtils.CONSISTENCY_LOGGER .error("stopRetryTask error " + context.getInvocation() + " please check", t); } } //private void executeOnFailureMethod(RetryContext context) { // // MaxRetryTimesRetryContext maxRetryTimesRetryContext = (MaxRetryTimesRetryContext)context; // String methodName = maxRetryTimesRetryContext.getOnFailureMethod(); // if (StringUtils.isEmpty(methodName)) { // return; // } // Object executor = context.getExecutor(); // Method onFailure = BeanUtils.getMethod(methodName, executor.getClass()); // if (Objects.isNull(onFailure)) { // return; // } // try { // onFailure.invoke(executor, context); // } catch (Throwable t) { // LogUtils.CONSISTENCY_LOGGER.error( // "executeOnFailureMethod failed onFailureMethod = {}" // + PrintUtils.printCommonMethodInfo(context) // + " please check", // methodName, // t); // } //} } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/PersistenceRetryer.java ================================================ package com.alibaba.easyretry.core; import com.alibaba.easyretry.common.SCallable; import com.alibaba.easyretry.common.processor.AsyncPersistenceProcessor; import com.alibaba.easyretry.common.retryer.Retryer; import com.alibaba.easyretry.common.retryer.RetryerInfo; import com.alibaba.easyretry.core.process.async.before.ExceptionPersistenceAsyncBeforeRetryProcessor; import com.alibaba.easyretry.core.process.async.before.ResultAsynPersistenceBeforeRetryProcessor; import lombok.Data; import lombok.extern.slf4j.Slf4j; /** * @author Created by wuhao on 2020/11/1. */ @Slf4j @Data public class PersistenceRetryer implements Retryer { private RetryerInfo retryerInfo; public PersistenceRetryer(RetryerInfo retryerInfo) { this.retryerInfo = retryerInfo; } @Override public V call(SCallable callable) throws Throwable { AsyncPersistenceProcessor asynPersistenceProcessor; try { V result = callable.call(); asynPersistenceProcessor = new ResultAsynPersistenceBeforeRetryProcessor<>(result, retryerInfo); } catch (Throwable e) { log.error( "call method error executorMethodName is {} executorName name is {} args is {}", retryerInfo.getExecutorMethodName(), retryerInfo.getExecutorName(), retryerInfo.getArgs(), e); asynPersistenceProcessor = new ExceptionPersistenceAsyncBeforeRetryProcessor<>(e, retryerInfo); } asynPersistenceProcessor.process(); return asynPersistenceProcessor.getResult(); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/PersistenceRetryerBuilder.java ================================================ package com.alibaba.easyretry.core; import com.alibaba.easyretry.common.AbstractResultPredicate; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.retryer.RetryerInfo; /** * @author Created by wuhao on 2020/11/1. */ public class PersistenceRetryerBuilder { private PersistenceRetryer persistenceRetryer; public PersistenceRetryerBuilder(RetryConfiguration retryConfiguration) { RetryerInfo retryerInfo = new RetryerInfo<>(); persistenceRetryer = new PersistenceRetryer(retryerInfo); } public static PersistenceRetryerBuilder of(RetryConfiguration retryConfiguration) { return new PersistenceRetryerBuilder<>(retryConfiguration); } public PersistenceRetryerBuilder withExecutorName(String executorName) { persistenceRetryer.getRetryerInfo().setExecutorName(executorName); return this; } public PersistenceRetryerBuilder withExecutorMethodName(String executorMethodName) { persistenceRetryer.getRetryerInfo().setExecutorMethodName(executorMethodName); return this; } public PersistenceRetryerBuilder withBizId(String bizId) { persistenceRetryer.getRetryerInfo().setBizId(bizId); return this; } public PersistenceRetryerBuilder withArgs(Object[] args) { persistenceRetryer.getRetryerInfo().setArgs(args); return this; } public PersistenceRetryerBuilder withOnException(Class onException) { persistenceRetryer.getRetryerInfo().setOnException(onException); return this; } public PersistenceRetryerBuilder withOnFailureMethod(String onFailureMethod) { persistenceRetryer.getRetryerInfo().setOnFailureMethod(onFailureMethod); return this; } public PersistenceRetryerBuilder withReThrowException(boolean reThrowException) { persistenceRetryer.getRetryerInfo().setReThrowException(reThrowException); return this; } public PersistenceRetryerBuilder withNamespace(String namespace) { persistenceRetryer.getRetryerInfo().setNamespace(namespace); return this; } public PersistenceRetryerBuilder withConfiguration(RetryConfiguration retryConfiguration) { persistenceRetryer.getRetryerInfo().setRetryConfiguration(retryConfiguration); return this; } public PersistenceRetryerBuilder withResultPredicate(AbstractResultPredicate abstractResultPredicate) { persistenceRetryer.getRetryerInfo().setResultPredicate(abstractResultPredicate); return this; } public PersistenceRetryer build() { return persistenceRetryer; } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/RetryerBuilder.java ================================================ package com.alibaba.easyretry.core; import com.alibaba.easyretry.common.AbstractResultPredicate; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.constant.enums.RetryTypeEnum; import com.alibaba.easyretry.common.retryer.Retryer; /** * @author Created by zhangchi on 2023-07-13 */ public class RetryerBuilder { /** * 执行者名称 */ private String executorNameContext; /** * 执行者方法 */ private String executorMethodNameContext; private String onFailureMethodContext; /** * 业务id,外部可以自定义存储一些信息 */ private String bizIdContext; private Object[] argsContext; private Class onExceptionContext; private RetryConfiguration retryConfigurationContext; private String namespaceContext; private boolean reThrowExceptionContext; private AbstractResultPredicate resultPredicateContext; public RetryerBuilder withExecutorName(String executorName) { executorNameContext = executorName; return this; } public RetryerBuilder withExecutorMethodName(String executorMethodName) { executorMethodNameContext = executorMethodName; return this; } public RetryerBuilder withBizId(String bizId) { bizIdContext = bizId; return this; } public RetryerBuilder withArgs(Object[] args) { argsContext = args; return this; } public RetryerBuilder withOnException(Class onException) { onExceptionContext = onException; return this; } public RetryerBuilder withOnFailureMethod(String onFailureMethod) { onFailureMethodContext = onFailureMethod; return this; } public RetryerBuilder withReThrowException(boolean reThrowException) { reThrowExceptionContext = reThrowException; return this; } public RetryerBuilder withNamespace(String namespace) { namespaceContext = namespace; return this; } public RetryerBuilder withConfiguration(RetryConfiguration retryConfiguration) { retryConfigurationContext = retryConfiguration; return this; } public RetryerBuilder withResultPredicate(AbstractResultPredicate abstractResultPredicate) { resultPredicateContext = abstractResultPredicate; return this; } public Retryer build(RetryTypeEnum retryTypeEnum) { if (RetryTypeEnum.SYNC == retryTypeEnum) { return buildSyncRetryer(); } else { return buildAsyncRetryer(); } } private SyncRetryer buildSyncRetryer() { SyncRetryerBuilder builder = SyncRetryerBuilder.of(retryConfigurationContext) .withConfiguration(retryConfigurationContext); return builder.build(); } private PersistenceRetryer buildAsyncRetryer() { PersistenceRetryerBuilder builder = PersistenceRetryerBuilder.of(retryConfigurationContext) .withExecutorName(executorNameContext) .withExecutorMethodName(executorMethodNameContext) .withArgs(argsContext) .withConfiguration(retryConfigurationContext) // .withOnFailureMethod(retryable.onFailureMethod()) // .withNamespace(namespace) .withReThrowException(reThrowExceptionContext) .withResultPredicate(resultPredicateContext); return builder.build(); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/SyncRetryer.java ================================================ package com.alibaba.easyretry.core; import com.alibaba.easyretry.common.AbstractRetrySyncExecutor; import com.alibaba.easyretry.common.SCallable; import com.alibaba.easyretry.common.retryer.Retryer; import com.alibaba.easyretry.common.retryer.RetryerInfo; import lombok.Data; import lombok.extern.slf4j.Slf4j; /** * @author Created by zhangchi Created on 2023-07-12 */ @Slf4j @Data public class SyncRetryer implements Retryer { private RetryerInfo retryerInfo; public SyncRetryer(RetryerInfo retryerInfo) { this.retryerInfo = retryerInfo; } @Override public V call(SCallable callable) throws Throwable { AbstractRetrySyncExecutor retrySyncExecutor = retryerInfo.getRetryConfiguration().getRetrySyncExecutor(); retrySyncExecutor.setRetryerInfo(retryerInfo); return (V) retrySyncExecutor.call(callable); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/SyncRetryerBuilder.java ================================================ package com.alibaba.easyretry.core; import java.util.Objects; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.retryer.RetryerInfo; /** * @author Created by zhangchi on 2023-07-12 */ public class SyncRetryerBuilder { private SyncRetryer syncRetryer; public SyncRetryerBuilder(RetryConfiguration retryConfiguration) { RetryerInfo retryerInfo = new RetryerInfo<>(); syncRetryer = new SyncRetryer<>(retryerInfo); } public static SyncRetryerBuilder of(RetryConfiguration retryConfiguration) { return new SyncRetryerBuilder<>(retryConfiguration); } public SyncRetryerBuilder withConfiguration(RetryConfiguration retryConfiguration) { syncRetryer.getRetryerInfo().setRetryConfiguration(retryConfiguration); return this; } public SyncRetryer build() { return syncRetryer; } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/access/DefaultRetrySerializerAccess.java ================================================ package com.alibaba.easyretry.core.access; import com.alibaba.easyretry.common.access.RetrySerializerAccess; import com.alibaba.easyretry.common.serializer.RetryArgSerializer; import com.alibaba.easyretry.core.serializer.HessianRetryArgSerializer; /** * @author Created by wuhao on 2020/11/6. */ public class DefaultRetrySerializerAccess implements RetrySerializerAccess { @Override public RetryArgSerializer getCurrentGlobalRetrySerializer() { return new HessianRetryArgSerializer(); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/access/MemoryRetryTaskAccess.java ================================================ package com.alibaba.easyretry.core.access; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import com.alibaba.easyretry.common.access.RetryTaskAccess; import com.alibaba.easyretry.common.constant.enums.RetryTaskStatusEnum; import com.alibaba.easyretry.common.entity.RetryTask; import com.google.common.collect.Maps; /** * @author Created by wuhao on 2020/11/5. */ public class MemoryRetryTaskAccess implements RetryTaskAccess { private static final Map retryTaskMap = Maps.newConcurrentMap(); private static final AtomicLong atomicLong = new AtomicLong(); @Override public boolean saveRetryTask(RetryTask retryTask) { long id = atomicLong.getAndIncrement(); retryTask.setId(id); retryTaskMap.putIfAbsent(id, retryTask); return true; } @Override public boolean handlingRetryTask(RetryTask retryTask) { retryTask.setStatus(RetryTaskStatusEnum.HANDLING); retryTaskMap.putIfAbsent(retryTask.getId(), retryTask); return true; } @Override public boolean finishRetryTask(RetryTask retryTask) { retryTaskMap.remove(retryTask.getId()); return true; } @Override public boolean stopRetryTask(RetryTask retryTask) { retryTask.setStatus(RetryTaskStatusEnum.ERROR); retryTaskMap.putIfAbsent(retryTask.getId(), retryTask); return false; } @Override public List listAvailableTasks(Long lastId) { return retryTaskMap.values().stream() .filter((retryTask) -> retryTask.getStatus() == RetryTaskStatusEnum.INIT) .filter((retryTask) -> retryTask.getId() > lastId) .sorted((o1, o2) -> o1.getId() > o2.getId() ? 1 : -1) .collect(Collectors.toList()); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/container/SimpleRetryContainer.java ================================================ package com.alibaba.easyretry.core.container; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.RetryContainer; import com.alibaba.easyretry.common.RetryContext; import com.alibaba.easyretry.common.RetryExecutor; import com.alibaba.easyretry.common.constant.enums.HandleResultEnum; import com.alibaba.easyretry.common.entity.RetryTask; import com.alibaba.easyretry.core.context.MaxAttemptsPersistenceRetryContext.RetryContextBuilder; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; /** * @author Created by wuhao on 2020/11/5. */ @Slf4j public class SimpleRetryContainer implements RetryContainer { private static final Integer MAX_QUEUE_SIZE = 2000; private RetryConfiguration retryConfiguration; private RetryExecutor retryExecutor; public SimpleRetryContainer() { } public SimpleRetryContainer( RetryConfiguration retryConfiguration, RetryExecutor retryExecutor) { this.retryConfiguration = retryConfiguration; this.retryExecutor = retryExecutor; } @Override public void start() { BlockingQueue queue = new PriorityBlockingQueue<>(MAX_QUEUE_SIZE); ThreadPoolExecutor retryExecutor = new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), r -> new Thread(r, "retryExecutor-Thread")); retryExecutor.execute(new TaskConsumer(queue)); ThreadPoolExecutor retrySelector = new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), r -> new Thread(r, "retrySelector-Thread")); retrySelector.execute(new TaskProducer(queue)); } @Override public void stop() { } public class TaskConsumer implements Runnable { private static final long MAX_SLEEP_TIME_MILLISECONDS = 10 * 1000L; private static final long SLEEP_BASE_TIME_MILLISECONDS = 1000L; private final BlockingQueue queue; private long sleepTimes = 0L; private TaskConsumer(BlockingQueue queue) { this.queue = queue; } @Override public void run() { while (!Thread.currentThread().isInterrupted()) { doExecute(); long totalTime = sleepTimes * SLEEP_BASE_TIME_MILLISECONDS; try { TimeUnit.MILLISECONDS.sleep(Math.min(totalTime, MAX_SLEEP_TIME_MILLISECONDS)); } catch (InterruptedException e) { log.error("taskConsumer interruptedException error", e); } } } private void doExecute() { try { RetryContext context = queue.take(); HandleResultEnum result = retryExecutor.doExecute(context); if (HandleResultEnum.SUCCESS == result) { sleepTimes = 0L; } else if (HandleResultEnum.FAILURE == result) { sleepTimes = 0L; queue.add(context); } else if (HandleResultEnum.STOP == result) { sleepTimes = 0L; } else if (HandleResultEnum.ERROR == result) { sleepTimes = 0L; // do nothing } else { sleepTimes++; queue.add(context); } } catch (InterruptedException e) { log.error("Retry execute failed when getting retry task", e); } catch (Throwable e) { log.error("Retry invoke failed", e); } } } public class TaskProducer implements Runnable { private static final long MAX_SLEEP_TIME_MILLISECONDS = 10 * 1000L; private static final long SLEEP_BASE_TIME_MILLISECONDS = 1000L; private final BlockingQueue queue; private long sleepTimes = 0L; private volatile Long lastId = -1L; public TaskProducer(BlockingQueue queue) { this.queue = queue; } @Override public void run() { while (!Thread.currentThread().isInterrupted()) { doSelect(); long totalTime = sleepTimes * SLEEP_BASE_TIME_MILLISECONDS + SLEEP_BASE_TIME_MILLISECONDS; try { TimeUnit.MILLISECONDS.sleep(Math.min(totalTime, MAX_SLEEP_TIME_MILLISECONDS)); } catch (InterruptedException e) { log.error("taskConsumer interruptedException error", e); } } } private void doSelect() { if (queue.size() >= MAX_QUEUE_SIZE) { sleepTimes++; return; } List tasks = retryConfiguration.getRetryTaskAccess().listAvailableTasks(lastId); if (CollectionUtils.isEmpty(tasks)) { sleepTimes++; return; } if (queue.size() >= MAX_QUEUE_SIZE) { sleepTimes++; return; } if (queue.size() + tasks.size() >= MAX_QUEUE_SIZE) { sleepTimes++; } else { sleepTimes = 0L; } for (RetryTask task : tasks) { try { lastId = task.getId(); RetryContext retryContext = new RetryContextBuilder(retryConfiguration, task) .buildInvocation() .buildRetryArgSerializer() .buildStopStrategy() .buildWaitStrategy() .buildRetryTask() .buildMaxRetryTimes() .buildOnFailureMethod() .buildPriority(0L) .buildResultPredicateSerializer() .build(); retryContext.start(); queue.put(retryContext); log.warn("add retry task to queue, task:{}", task.getId()); } catch (Throwable e) { log.error("add retry task to queue , task:{}", task.getId(), e); // 出现异常task将放不进queue中,就不会再重试了 // 是否需要更新task的状态,并且加上失败的原因? } } } } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/context/MaxAttemptsPersistenceRetryContext.java ================================================ package com.alibaba.easyretry.core.context; import java.lang.reflect.Method; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import com.alibaba.easyretry.common.Invocation; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.RetryContext; import com.alibaba.easyretry.common.RetryLifecycle; import com.alibaba.easyretry.common.SimpleMethodInvocation; import com.alibaba.easyretry.common.entity.RetryTask; import com.alibaba.easyretry.common.serializer.ResultPredicateSerializer; import com.alibaba.easyretry.common.serializer.RetryArgSerializer; import com.alibaba.easyretry.common.strategy.StopStrategy; import com.alibaba.easyretry.common.strategy.WaitStrategy; import com.google.common.collect.Maps; import lombok.Data; import lombok.ToString; import org.apache.commons.lang3.reflect.MethodUtils; @Data @ToString(callSuper = true) public class MaxAttemptsPersistenceRetryContext implements RetryContext, RetryLifecycle, Comparable { private RetryTask retryTask; private RetryArgSerializer retryArgSerializer; private ResultPredicateSerializer resultPredicateSerializer; private Long priority; private StopStrategy stopStrategy; private WaitStrategy waitStrategy; private int maxRetryTimes; private String onFailureMethod; private Invocation invocation; @Override public int compareTo(MaxAttemptsPersistenceRetryContext o) { return this.priority > o.getPriority() ? 1 : -1; } @Override public void start() { } @Override public void stop() { stopStrategy.clear(this); waitStrategy.clear(this); } @Override public void setAttribute(String key, String value) { Map extAttrs = retryTask.getExtAttrs(); if (Objects.isNull(extAttrs)) { extAttrs = Maps.newHashMap(); } extAttrs.put(key, value); } @Override public String getAttribute(String key) { Map extAttrs = retryTask.getExtAttrs(); if (Objects.isNull(extAttrs)) { return null; } else { return extAttrs.get(key); } } public Long getNextRetryTime(TimeUnit unit) { return unit.convert(priority, TimeUnit.MILLISECONDS); } public void setNextRetryTime(Long nexRetryTime, TimeUnit unit) { priority = unit.toMillis(nexRetryTime); } @Override public String getId() { return retryTask.getId() + ""; } public static class RetryContextBuilder { private MaxAttemptsPersistenceRetryContext retryContext; private RetryConfiguration retryConfiguration; private RetryTask retryTask; public RetryContextBuilder(RetryConfiguration retryConfiguration, RetryTask retryTask) { retryContext = new MaxAttemptsPersistenceRetryContext(); this.retryConfiguration = retryConfiguration; this.retryTask = retryTask; } public RetryContextBuilder buildInvocation() { RetryArgSerializer retryArgSerializer = retryConfiguration.getRetrySerializerAccess() .getCurrentGlobalRetrySerializer(); Object[] args = retryArgSerializer.deSerialize(retryTask.getArgsStr()).getArgs(); Object executor = retryConfiguration.getExecutorSolver() .resolver(retryTask.getExecutorName()); Class[] classes = Stream.of(args).map(Object::getClass).toArray(Class[]::new); Method method = MethodUtils .getMatchingMethod(executor.getClass(), retryTask.getExecutorMethodName(), classes); SimpleMethodInvocation simpleMethodInvocation = new SimpleMethodInvocation(executor, method, args); retryContext.setInvocation(simpleMethodInvocation); return this; } public RetryContextBuilder buildRetryArgSerializer() { retryContext.setRetryArgSerializer( retryConfiguration.getRetrySerializerAccess().getCurrentGlobalRetrySerializer()); return this; } public RetryContextBuilder buildStopStrategy() { retryContext.setStopStrategy( retryConfiguration.getRetryStrategyAccess().getCurrentGlobalStopStrategy()); return this; } public RetryContextBuilder buildWaitStrategy() { retryContext.setWaitStrategy( retryConfiguration.getRetryStrategyAccess().getCurrentGlobalWaitStrategy()); return this; } public RetryContextBuilder buildRetryTask() { retryContext.setRetryTask(retryTask); return this; } public RetryContextBuilder buildMaxRetryTimes() { retryContext.setMaxRetryTimes(retryConfiguration.getMaxRetryTimes()); return this; } public RetryContextBuilder buildOnFailureMethod() { retryContext.setOnFailureMethod(retryTask.getOnFailureMethod()); return this; } public RetryContextBuilder buildResultPredicateSerializer() { retryContext .setResultPredicateSerializer(retryConfiguration.getResultPredicateSerializer()); return this; } public RetryContextBuilder buildPriority(Long priority) { retryContext.setPriority(priority); return this; } public MaxAttemptsPersistenceRetryContext build() { return retryContext; } } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/degrade/EasyRetryDegradeHelper.java ================================================ package com.alibaba.easyretry.core.degrade; import com.alibaba.easyretry.common.RetryContext; /** * @author Created by gejinfeng on 2021/4/29. */ public interface EasyRetryDegradeHelper { /** * 是否降级 * * @param retryContext retryContext * @return degrade */ boolean degrade(RetryContext retryContext); } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/event/SimpleRetryEventMulticaster.java ================================================ package com.alibaba.easyretry.core.event; import java.util.List; import java.util.Objects; import com.alibaba.easyretry.common.event.RetryEvent; import com.alibaba.easyretry.common.event.RetryEventMulticaster; import com.alibaba.easyretry.common.event.RetryListener; import com.google.common.collect.Lists; import lombok.Setter; /** * @author Created by wuhao on 2021/3/26. */ public class SimpleRetryEventMulticaster implements RetryEventMulticaster { @Setter private List listenerCaches = Lists.newArrayList(); @Override public void register(RetryListener listener) { listenerCaches.add(listener); } @Override public void multicast(RetryEvent retryEvent) { if (Objects.isNull(retryEvent)) { return; } listenerCaches.forEach((retryListener) -> retryListener.onRetryEvent(retryEvent)); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/filter/DefaultRetryFilterInvocationHandler.java ================================================ package com.alibaba.easyretry.core.filter; import java.util.List; import com.alibaba.easyretry.common.RetryContext; import com.alibaba.easyretry.common.filter.RetryFilter; import com.alibaba.easyretry.common.filter.RetryFilterInvocation; import com.alibaba.easyretry.common.filter.RetryFilterInvocationHandler; import com.alibaba.easyretry.common.filter.RetryFilterRegister; import com.alibaba.easyretry.common.filter.RetryFilterResponse; import lombok.Setter; import org.apache.commons.collections4.CollectionUtils; /** * @author Created by wuhao on 2021/3/22. */ public class DefaultRetryFilterInvocationHandler implements RetryFilterInvocationHandler, RetryFilterInvocation { private RetryFilter firstFilter; @Setter private RetryFilterRegister retryFilterRegister; @Override public RetryFilterResponse invoke(RetryContext retryContext) throws Throwable { return firstFilter.doFilter(retryContext); } @Override public void handle() { List retryFilters = retryFilterRegister.export(); firstFilter = new NOOPRetryFilter(); RetryFilter lastRetryFilter = firstFilter; for (RetryFilter retryFilter : CollectionUtils.emptyIfNull(retryFilters)) { lastRetryFilter.setNext(retryFilter); lastRetryFilter = retryFilter; } IdentifyRetryFilter identifyRetryFilter = new IdentifyRetryFilter(); lastRetryFilter.setNext(identifyRetryFilter); lastRetryFilter = identifyRetryFilter; MethodExcuteRetryFilter methodExcuteRetryFilter = new MethodExcuteRetryFilter(); lastRetryFilter.setNext(methodExcuteRetryFilter); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/filter/DefaultRetryFilterRegisterHandler.java ================================================ package com.alibaba.easyretry.core.filter; import java.util.List; import com.alibaba.easyretry.common.filter.RetryFilter; import com.alibaba.easyretry.common.filter.RetryFilterDiscover; import com.alibaba.easyretry.common.filter.RetryFilterRegister; import com.alibaba.easyretry.common.filter.RetryFilterRegisterHandler; import lombok.Setter; import org.apache.commons.collections4.CollectionUtils; /** * @author Created by wuhao on 2021/4/9. */ public class DefaultRetryFilterRegisterHandler implements RetryFilterRegisterHandler { @Setter private RetryFilterDiscover retryFilterDiscover; @Setter private RetryFilterRegister retryFilterRegister; @Override public void handle() { List retryFilters = retryFilterDiscover.discoverAll(); for (RetryFilter retryFilter : CollectionUtils.emptyIfNull(retryFilters)) { retryFilterRegister.register(retryFilter); } } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/filter/IdentifyRetryFilter.java ================================================ package com.alibaba.easyretry.core.filter; import com.alibaba.easyretry.common.RetryContext; import com.alibaba.easyretry.common.RetryIdentify; import com.alibaba.easyretry.common.filter.AbstractRetryFilter; import com.alibaba.easyretry.common.filter.RetryFilterResponse; /** * @author Created by wuhao on 2021/3/22. */ public class IdentifyRetryFilter extends AbstractRetryFilter { @Override public RetryFilterResponse doFilter(RetryContext retryContext) throws Throwable { try { RetryIdentify.start(); return next.doFilter(retryContext); } finally { RetryIdentify.stop(); } } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/filter/MethodExcuteRetryFilter.java ================================================ package com.alibaba.easyretry.core.filter; import com.alibaba.easyretry.common.RetryContext; import com.alibaba.easyretry.common.filter.RetryFilter; import com.alibaba.easyretry.common.filter.RetryFilterResponse; /** * @author Created by wuhao on 2021/3/22. */ public class MethodExcuteRetryFilter implements RetryFilter { @Override public RetryFilterResponse doFilter(RetryContext retryContext) throws Throwable { RetryFilterResponse retryFilterResponse = new RetryFilterResponse(); retryFilterResponse.setResponse(retryContext.getInvocation().invoke()); return retryFilterResponse; } @Override public void setNext(RetryFilter next) { } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/filter/NOOPRetryFilter.java ================================================ package com.alibaba.easyretry.core.filter; import com.alibaba.easyretry.common.RetryContext; import com.alibaba.easyretry.common.filter.AbstractRetryFilter; import com.alibaba.easyretry.common.filter.RetryFilterResponse; /** * @author Created by wuhao on 2021/3/22. */ public class NOOPRetryFilter extends AbstractRetryFilter { @Override public RetryFilterResponse doFilter(RetryContext retryContext) throws Throwable { return next.doFilter(retryContext); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/filter/SPIRetryFilterDiscover.java ================================================ package com.alibaba.easyretry.core.filter; import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; import com.alibaba.easyretry.common.filter.RetryFilter; import com.alibaba.easyretry.common.filter.RetryFilterDiscover; import com.google.common.collect.Lists; /** * @author Created by wuhao on 2021/4/9. */ public class SPIRetryFilterDiscover implements RetryFilterDiscover { @Override public List discoverAll() { ServiceLoader retryFilters = ServiceLoader.load(RetryFilter.class); Iterator iterator = retryFilters.iterator(); return Lists.newArrayList(iterator); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/filter/SimpleRetryFilterRegister.java ================================================ package com.alibaba.easyretry.core.filter; import java.util.List; import com.alibaba.easyretry.common.filter.RetryFilter; import com.alibaba.easyretry.common.filter.RetryFilterRegister; import com.google.common.collect.Lists; /** * @author Created by wuhao on 2021/4/9. */ public class SimpleRetryFilterRegister implements RetryFilterRegister { private List retryFiltersCache = Lists.newArrayList(); @Override public void register(RetryFilter retryFilter) { retryFiltersCache.add(retryFilter); } @Override public List export() { return retryFiltersCache; } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/process/async/AbstractAsyncPersistenceProcessor.java ================================================ package com.alibaba.easyretry.core.process.async; import com.alibaba.easyretry.common.processor.AsyncPersistenceProcessor; import lombok.extern.slf4j.Slf4j; /** * @author Created by wuhao on 2021/3/19. */ @Slf4j public abstract class AbstractAsyncPersistenceProcessor implements AsyncPersistenceProcessor { @Override public void process() { if (!needRetry()) { return; } doProcess(); } protected abstract void doProcess(); @Override public abstract boolean needRetry(); } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/process/async/before/AbstractAsyncPersistenceBeforeRetryProcessor.java ================================================ package com.alibaba.easyretry.core.process.async.before; import java.util.Date; import java.util.Map; import java.util.Objects; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.constant.enums.RetryTaskStatusEnum; import com.alibaba.easyretry.common.entity.RetryTask; import com.alibaba.easyretry.common.event.before.AfterSaveBeforeRetryEvent; import com.alibaba.easyretry.common.event.before.PrepSaveBeforeRetryEvent; import com.alibaba.easyretry.common.retryer.RetryerInfo; import com.alibaba.easyretry.common.serializer.ArgSerializerInfo; import com.alibaba.easyretry.core.process.async.AbstractAsyncPersistenceProcessor; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; /** * @author Created by wuhao on 2021/3/19. */ @Slf4j public abstract class AbstractAsyncPersistenceBeforeRetryProcessor extends AbstractAsyncPersistenceProcessor { protected RetryerInfo retryerInfo; // private RetryConfiguration retryConfiguration; public AbstractAsyncPersistenceBeforeRetryProcessor( RetryerInfo retryerInfo) { this.retryerInfo = retryerInfo; // this.retryConfiguration = retryConfiguration; } @Override public void doProcess() { RetryConfiguration retryConfiguration = retryerInfo.getRetryConfiguration(); ArgSerializerInfo argSerializerInfo = new ArgSerializerInfo(); argSerializerInfo.setArgs(retryerInfo.getArgs()); argSerializerInfo.setExecutorMethodName(retryerInfo.getExecutorMethodName()); argSerializerInfo.setExecutorName(retryerInfo.getExecutorName()); String argsStr = retryConfiguration.getRetrySerializerAccess() .getCurrentGlobalRetrySerializer().serialize(argSerializerInfo); RetryTask retryTask = new RetryTask(); retryTask.setBizId(retryerInfo.getBizId()); retryTask.setArgsStr(argsStr); retryTask.setStatus(RetryTaskStatusEnum.INIT); retryTask.setExecutorMethodName(retryerInfo.getExecutorMethodName()); retryTask.setExecutorName(retryerInfo.getExecutorName()); retryTask.setOnFailureMethod(retryerInfo.getOnFailureMethod()); retryTask.setGmtCreate(new Date()); retryTask.setGmtModified(new Date()); Map extAttrs = Maps.newHashMap(); if (Objects.nonNull(retryerInfo.getResultPredicate())) { extAttrs.put("resultPredicateSerializer", retryConfiguration.getResultPredicateSerializer() .serialize(retryerInfo.getResultPredicate())); } retryTask.setExtAttrs(extAttrs); PrepSaveBeforeRetryEvent prepSaveBeforeRetryEvent = new PrepSaveBeforeRetryEvent(retryTask); retryConfiguration.getRetryEventMulticaster().multicast(prepSaveBeforeRetryEvent); retryConfiguration.getRetryTaskAccess().saveRetryTask(retryTask); AfterSaveBeforeRetryEvent afterSaveBeforeRetryEvent = new AfterSaveBeforeRetryEvent( retryTask); retryConfiguration.getRetryEventMulticaster().multicast(afterSaveBeforeRetryEvent); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/process/async/before/ExceptionPersistenceAsyncBeforeRetryProcessor.java ================================================ package com.alibaba.easyretry.core.process.async.before; import java.util.Objects; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.retryer.RetryerInfo; import org.apache.commons.lang3.ClassUtils; /** * @author Created by wuhao on 2021/3/19. */ public class ExceptionPersistenceAsyncBeforeRetryProcessor extends AbstractAsyncPersistenceBeforeRetryProcessor { private Throwable throwable; public ExceptionPersistenceAsyncBeforeRetryProcessor(Throwable throwable, RetryerInfo retryerInfo) { super(retryerInfo); this.throwable = throwable; } @Override public boolean needRetry() { Class onException = retryerInfo.getOnException(); if (Objects.isNull(onException)) { return true; } return ClassUtils.isAssignable(retryerInfo.getOnException(), throwable.getClass()); } @Override public R getResult() throws Throwable { if (retryerInfo.isReThrowException()) { throw throwable; } else { return null; } } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/process/async/before/ResultAsynPersistenceBeforeRetryProcessor.java ================================================ package com.alibaba.easyretry.core.process.async.before; import java.util.Objects; import com.alibaba.easyretry.common.AbstractResultPredicate; import com.alibaba.easyretry.common.retryer.RetryerInfo; /** * @author Created by wuhao on 2021/3/19. */ public class ResultAsynPersistenceBeforeRetryProcessor extends AbstractAsyncPersistenceBeforeRetryProcessor { private final R result; public ResultAsynPersistenceBeforeRetryProcessor(R result, RetryerInfo retryerInfo) { super(retryerInfo); this.result = result; } @Override public boolean needRetry() { AbstractResultPredicate easyRetryPredicate = retryerInfo.getResultPredicate(); if (Objects.nonNull(easyRetryPredicate)) { return easyRetryPredicate.apply(result); } else { return false; } } @Override public R getResult() { return result; } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/process/async/on/AbstractAsyncPersistenceOnRetryProcessor.java ================================================ package com.alibaba.easyretry.core.process.async.on; import com.alibaba.easyretry.common.constant.enums.HandleResultEnum; import com.alibaba.easyretry.core.context.MaxAttemptsPersistenceRetryContext; import com.alibaba.easyretry.core.process.async.AbstractAsyncPersistenceProcessor; import lombok.extern.slf4j.Slf4j; /** * @author Created by wuhao on 2021/3/19. */ @Slf4j public abstract class AbstractAsyncPersistenceOnRetryProcessor extends AbstractAsyncPersistenceProcessor { protected MaxAttemptsPersistenceRetryContext context; private HandleResultEnum retryResult; public AbstractAsyncPersistenceOnRetryProcessor(MaxAttemptsPersistenceRetryContext context) { this.context = context; } @Override public void process() { if (!needRetry()) { retryResult = HandleResultEnum.SUCCESS; return; } doProcess(); } @Override public void doProcess() { if (context.getStopStrategy().shouldStop(context)) { log.error(context.getInvocation() + " will stop"); retryResult = HandleResultEnum.STOP; } else { log.error(context.getInvocation() + " will try later"); context.getWaitStrategy().backOff(context); retryResult = HandleResultEnum.FAILURE; } } public HandleResultEnum getRetryResult() { return retryResult; } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/process/async/on/ExceptionPersistenceAsynOnRetryProcessor.java ================================================ package com.alibaba.easyretry.core.process.async.on; import com.alibaba.easyretry.core.context.MaxAttemptsPersistenceRetryContext; /** * @author Created by wuhao on 2021/3/19. */ public class ExceptionPersistenceAsynOnRetryProcessor extends AbstractAsyncPersistenceOnRetryProcessor { private final Throwable throwable; public ExceptionPersistenceAsynOnRetryProcessor(Throwable throwable, MaxAttemptsPersistenceRetryContext context) { super(context); this.throwable = throwable; } @Override public boolean needRetry() { return true; } @Override public R getResult() { return null; } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/process/async/on/ResultAsynPersistenceOnRetryProcessor.java ================================================ package com.alibaba.easyretry.core.process.async.on; import com.alibaba.easyretry.common.AbstractResultPredicate; import com.alibaba.easyretry.core.context.MaxAttemptsPersistenceRetryContext; import org.apache.commons.lang3.StringUtils; /** * @author Created by wuhao on 2021/3/19. */ public class ResultAsynPersistenceOnRetryProcessor extends AbstractAsyncPersistenceOnRetryProcessor { private R result; public ResultAsynPersistenceOnRetryProcessor(R result, MaxAttemptsPersistenceRetryContext context) { super(context); this.result = result; } @Override public boolean needRetry() { String resultPredicateSerializerStr = context.getAttribute("resultPredicateSerializer"); if (StringUtils.isBlank(resultPredicateSerializerStr)) { return false; } AbstractResultPredicate resultPredicate = context.getResultPredicateSerializer() .deSerialize(resultPredicateSerializerStr); return resultPredicate.apply(result); } @Override public R getResult() { return result; } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/process/package-info.java ================================================ /** * @author Created by wuhao on 2021/3/19. */ package com.alibaba.easyretry.core.process; ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/serializer/FastJsonRetryArgSerializer.java ================================================ package com.alibaba.easyretry.core.serializer; import java.util.stream.Stream; import com.alibaba.easyretry.common.serializer.ArgSerializerInfo; import com.alibaba.easyretry.common.serializer.RetryArgSerializer; import com.alibaba.fastjson.JSON; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; /** * */ public class FastJsonRetryArgSerializer implements RetryArgSerializer { public static final String SPLIT = "||"; public static final String INNER_SPLIT = "&&"; @Override public String serialize(ArgSerializerInfo argSerializerInfo) { StringBuilder sb = new StringBuilder(); Stream.of(argSerializerInfo.getArgs()).forEach( (arg) -> sb.append(JSON.toJSONString(arg)).append(INNER_SPLIT) .append(arg.getClass().getName()).append(SPLIT)); if (sb.length() >= SPLIT.length()) { return sb.subSequence(0, sb.length() - SPLIT.length()).toString(); } else { return null; } } @Override public ArgSerializerInfo deSerialize(String argsStr) { String[] strs = StringUtils.split(argsStr, SPLIT); Object[] arg = Stream.of(strs) .map((str) -> { String[] inner = str.split(INNER_SPLIT); try { return JSON.parseObject(inner[0], ClassUtils.getClass(inner[1])); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } }) .toArray(); ArgSerializerInfo argSerializerInfo = new ArgSerializerInfo(); argSerializerInfo.setArgs(arg); return argSerializerInfo; } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/serializer/HessianResultPredicateSerializer.java ================================================ package com.alibaba.easyretry.core.serializer; import com.alibaba.easyretry.common.AbstractResultPredicate; import com.alibaba.easyretry.common.serializer.ResultPredicateSerializer; import com.alibaba.easyretry.core.utils.HessianSerializerUtils; /** * @author Created by wuhao on 2021/3/18. */ public class HessianResultPredicateSerializer implements ResultPredicateSerializer { @Override public String serialize(AbstractResultPredicate serializeInfo) { return HessianSerializerUtils.serialize(serializeInfo); } @Override public AbstractResultPredicate deSerialize(String infoStr) { return HessianSerializerUtils.deSerialize(infoStr, AbstractResultPredicate.class); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/serializer/HessianRetryArgSerializer.java ================================================ package com.alibaba.easyretry.core.serializer; /** * @author Created by wuhao on 2021/2/22. */ import com.alibaba.easyretry.common.serializer.ArgSerializerInfo; import com.alibaba.easyretry.common.serializer.RetryArgSerializer; import com.alibaba.easyretry.core.utils.HessianSerializerUtils; import lombok.extern.slf4j.Slf4j; @Slf4j public class HessianRetryArgSerializer implements RetryArgSerializer { @Override public String serialize(ArgSerializerInfo argSerializerInfo) { Object[] args = argSerializerInfo.getArgs(); if (args.length == 0) { throw new IllegalStateException("No args found"); } return HessianSerializerUtils.serialize(args); } @Override public ArgSerializerInfo deSerialize(String argStr) { Object[] result = HessianSerializerUtils.deSerialize(argStr, Object[].class); ArgSerializerInfo argSerializerInfo = new ArgSerializerInfo(); argSerializerInfo.setArgs(result); return argSerializerInfo; } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/strategy/DefaultRetryStrategy.java ================================================ package com.alibaba.easyretry.core.strategy; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import com.alibaba.easyretry.common.RetryContext; import com.alibaba.easyretry.common.strategy.StopStrategy; import com.alibaba.easyretry.common.strategy.WaitStrategy; import com.alibaba.easyretry.core.context.MaxAttemptsPersistenceRetryContext; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; @Slf4j public class DefaultRetryStrategy implements StopStrategy, WaitStrategy { private static final Long MAX_INTERNAL_TIME = 15 * 60 * 1000L; private static final Long BASE_INTERNAL_TIME = 5000L; private final Map internalTimeMap = Maps.newConcurrentMap(); private final Map retryTimeMap = Maps.newConcurrentMap(); @Override public boolean shouldStop(RetryContext context) { MaxAttemptsPersistenceRetryContext maxAttemptsPersistenceRetryContext = (MaxAttemptsPersistenceRetryContext)context; Integer retryTimes = retryTimeMap.get(context.getId()); if (Objects.isNull(retryTimes)) { retryTimes = 1; } log.warn( "shouldStop retryTime is {} id is {} maxRetryTime is {}", retryTimes, context.getId(), maxAttemptsPersistenceRetryContext.getMaxRetryTimes()); return retryTimes >= maxAttemptsPersistenceRetryContext.getMaxRetryTimes(); } @Override public boolean shouldWait(RetryContext context) { MaxAttemptsPersistenceRetryContext maxAttemptsPersistenceRetryContext = (MaxAttemptsPersistenceRetryContext)context; internalTimeMap.putIfAbsent(context.getId(), 0L); Long priority = maxAttemptsPersistenceRetryContext.getNextRetryTime(TimeUnit.MILLISECONDS); if (Objects.isNull(priority)) { priority = 0L; } return System.currentTimeMillis() < priority; } @Override public void backOff(RetryContext context) { MaxAttemptsPersistenceRetryContext maxAttemptsPersistenceRetryContext = (MaxAttemptsPersistenceRetryContext)context; Integer retryTime = retryTimeMap.get(context.getId()); Long lastInternalTime = internalTimeMap.get(context.getId()); if (Objects.isNull(retryTime)) { retryTime = 1; } if (Objects.isNull(lastInternalTime)) { lastInternalTime = 0L; } long nextInternalTime = retryTime * (lastInternalTime + BASE_INTERNAL_TIME); nextInternalTime = Math.min(nextInternalTime, MAX_INTERNAL_TIME); internalTimeMap.put(context.getId(), nextInternalTime); retryTime++; retryTimeMap.put(context.getId(), retryTime); maxAttemptsPersistenceRetryContext .setNextRetryTime(System.currentTimeMillis() + nextInternalTime, TimeUnit.MILLISECONDS); log.warn( "backOff nextInternalTime is {} id is {} retryTime is {}", nextInternalTime, context.getId(), retryTime); } @Override public void clear(RetryContext context) { internalTimeMap.remove(context.getId()); retryTimeMap.remove(context.getId()); } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/utils/HessianSerializerUtils.java ================================================ package com.alibaba.easyretry.core.utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Base64; import com.caucho.hessian.io.HessianInput; import com.caucho.hessian.io.HessianOutput; /** * @author Created by wuhao on 2021/3/18. */ public class HessianSerializerUtils { public static String serialize(T t) { try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { HessianOutput ho = new HessianOutput(os); ho.writeObject(t); return Base64.getEncoder().encodeToString(os.toByteArray()); } catch (IOException e) { throw new IllegalStateException("HessianSerializationConverter.serialize failed.", e); } } public static T deSerialize(String str, Class tClass) { byte[] convertBytes = Base64.getDecoder().decode(str); try (ByteArrayInputStream is = new ByteArrayInputStream(convertBytes)) { HessianInput hi = new HessianInput(is); return (T)hi.readObject(tClass); } catch (IOException e) { throw new IllegalStateException("HessianSerializationConverter.deSerialize failed.", e); } } } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/utils/LogUtils.java ================================================ package com.alibaba.easyretry.core.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Created by wuhao on 2020/11/13. */ public class LogUtils { public static final Logger CONSISTENCY_LOGGER = LoggerFactory.getLogger("aRetryConsistency"); } ================================================ FILE: easy-retry-core/src/main/java/com/alibaba/easyretry/core/utils/PrintUtils.java ================================================ package com.alibaba.easyretry.core.utils; import com.alibaba.easyretry.common.RetryContext; import lombok.extern.slf4j.Slf4j; /** * @author Created by wuhao on 2020/11/14. */ @Slf4j public class PrintUtils { public static void monitorInfo(String action, RetryContext context) { monitorInfo(action, context, ""); } public static void monitorInfo(String action, RetryContext context, String extraInfo) { log.info(action + " arg is {} task id is {} " + extraInfo, context.getInvocation(), context.getId()); } } ================================================ FILE: easy-retry-core/src/test/java/com/alibaba/easyretry/core/utils/TestClass.java ================================================ package com.alibaba.easyretry.core.utils; class TestClass { public void say() { System.out.println("1"); } } ================================================ FILE: easy-retry-extensions/easy-retry-guava-extension/pom.xml ================================================ 4.0.0 com.alibaba easy-retry-extensions ${revision} ../pom.xml easy-retry-guava-extension easy-retry-guava-extension easy-retry-guava-extension ${project.groupId} easy-retry-common ${project.groupId} easy-retry-core com.google.guava guava com.github.rholder guava-retrying 2.0.0 ================================================ FILE: easy-retry-extensions/easy-retry-guava-extension/src/main/java/com/alibaba/easyretry/extension/guava/GuavaRetrySyncExecutor.java ================================================ package com.alibaba.easyretry.extension.guava; import java.util.Objects; import java.util.concurrent.TimeUnit; import com.alibaba.easyretry.common.AbstractResultPredicate; import com.alibaba.easyretry.common.AbstractRetrySyncExecutor; import com.alibaba.easyretry.common.SCallable; import com.alibaba.easyretry.common.retryer.RetryerInfo; import com.github.rholder.retry.Retryer; import com.github.rholder.retry.RetryerBuilder; import com.github.rholder.retry.StopStrategies; import com.github.rholder.retry.WaitStrategies; public class GuavaRetrySyncExecutor extends AbstractRetrySyncExecutor { private static final long WAIT_TIME = 1000L; @Override public V call(SCallable callable) throws Throwable { RetryerInfo retryerInfo = getRetryerInfo(); Class onException = Throwable.class; if (Objects.nonNull(retryerInfo.getOnException())) { onException = retryerInfo.getOnException(); } RetryerBuilder retryerBuilder = RetryerBuilder.newBuilder() .retryIfExceptionOfType(onException) .withStopStrategy(StopStrategies.stopAfterAttempt(retryerInfo.getRetryConfiguration().getMaxRetryTimes())) .withWaitStrategy(WaitStrategies.fixedWait(WAIT_TIME, TimeUnit.MILLISECONDS)); AbstractResultPredicate resultPredicate = retryerInfo.getResultPredicate(); if (Objects.nonNull(resultPredicate)) { retryerBuilder.retryIfResult(resultPredicate::apply); } Retryer retryer = retryerBuilder.build(); return retryer.call(() -> { try { return callable.call(); } catch (Throwable t) { throw new Exception(t); } }); } } ================================================ FILE: easy-retry-extensions/easy-retry-guava-extension/src/main/java/com/alibaba/easyretry/extension/guava/package-info.java ================================================ package com.alibaba.easyretry.extension.guava; ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/pom.xml ================================================ 4.0.0 com.alibaba easy-retry-extensions ${revision} ../pom.xml easy-retry-mybatis-extension easy-retry-mybatis-extension easy-retry-mybatis-extension 3.5.6 1.4.200 com.alibaba easy-retry-common org.mybatis mybatis ${mybatis.version} com.google.guava guava com.alibaba fastjson com.h2database h2 ${h2.version} test com.zaxxer HikariCP 4.0.3 test org.slf4j slf4j-api test ch.qos.logback logback-classic 1.2.3 test ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/main/java/com/alibaba/easyretry/extension/mybatis/access/MybatisRetryTaskAccess.java ================================================ package com.alibaba.easyretry.extension.mybatis.access; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import com.alibaba.easyretry.common.access.RetryTaskAccess; import com.alibaba.easyretry.common.constant.enums.RetryTaskStatusEnum; import com.alibaba.easyretry.common.entity.RetryTask; import com.alibaba.easyretry.extension.mybatis.common.utils.HostUtils; import com.alibaba.easyretry.extension.mybatis.dao.RetryTaskDAO; import com.alibaba.easyretry.extension.mybatis.po.RetryTaskPO; import com.alibaba.easyretry.extension.mybatis.query.RetryTaskQuery; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import lombok.AllArgsConstructor; /** * @author Created by wuhao on 2020/11/8. */ @AllArgsConstructor public class MybatisRetryTaskAccess implements RetryTaskAccess { private final RetryTaskDAO retryTaskDAO; @Override public boolean saveRetryTask(RetryTask retryTask) { RetryTaskPO retryTaskPO = covert(retryTask); return retryTaskDAO.saveRetryTask(retryTaskPO); } @Override public boolean handlingRetryTask(RetryTask retryTask) { return updateRetryTaskStatus(retryTask, RetryTaskStatusEnum.HANDLING); } @Override public boolean finishRetryTask(RetryTask retryTask) { RetryTaskPO retryTaskPO = new RetryTaskPO().setId(retryTask.getId()); return retryTaskDAO.deleteRetryTask(retryTaskPO); // return updateRetryTaskStatus(retryTask, RetryTaskStatusEnum.FINISH); } @Override public boolean stopRetryTask(RetryTask retryTask) { return updateRetryTaskStatus(retryTask, RetryTaskStatusEnum.ERROR); } private boolean updateRetryTaskStatus(RetryTask retryTask, RetryTaskStatusEnum status) { RetryTaskPO retryTaskPO = new RetryTaskPO(); retryTaskPO.setId(retryTask.getId()); retryTaskPO.setRetryStatus(status.getCode()); return retryTaskDAO.updateRetryTask(retryTaskPO); } @Override public List listAvailableTasks(Long lastId) { RetryTaskQuery retryTaskQuery = new RetryTaskQuery(); retryTaskQuery.setRetryStatus( Lists.newArrayList( RetryTaskStatusEnum.INIT.getCode(), RetryTaskStatusEnum.HANDLING.getCode())); retryTaskQuery.setLastId(lastId); retryTaskQuery.setSharding(HostUtils.getHostIP()); List retryTasks = retryTaskDAO.listRetryTask(retryTaskQuery); return convert(retryTasks); } private List convert(List retryTasks) { return retryTasks.stream().map(this::convert).collect(Collectors.toList()); } private RetryTask convert(RetryTaskPO retryTaskPO) { RetryTask retryTask = new RetryTask(); retryTask.setId(retryTaskPO.getId()); retryTask.setStatus(RetryTaskStatusEnum.fromCode(retryTaskPO.getRetryStatus())); retryTask.setArgsStr(retryTaskPO.getArgsStr()); retryTask.setGmtCreate(retryTaskPO.getGmtCreate()); retryTask.setGmtModified(retryTaskPO.getGmtModified()); retryTask.setExecutorName(retryTaskPO.getExecutorName()); retryTask.setExecutorMethodName(retryTaskPO.getExecutorMethodName()); retryTask.setBizId(retryTaskPO.getBizId()); retryTask.setExtAttrs(JSON.parseObject(retryTaskPO.getExtAttrs(), new TypeReference>() {})); return retryTask; } private RetryTaskPO covert(RetryTask retryTask) { RetryTaskPO retryTaskPO = new RetryTaskPO(); retryTaskPO.setId(retryTask.getId()); retryTaskPO.setSharding(HostUtils.getHostIP()); retryTaskPO.setBizId(retryTask.getBizId()); retryTaskPO.setExecutorName(retryTask.getExecutorName()); retryTaskPO.setExecutorMethodName(retryTask.getExecutorMethodName()); Map extAttrs = retryTask.getExtAttrs(); if (Objects.isNull(extAttrs)) { extAttrs = Maps.newHashMap(); } extAttrs.put("onFailureMethod", retryTask.getOnFailureMethod()); retryTaskPO.setExtAttrs(JSON.toJSONString(extAttrs)); retryTaskPO.setRetryStatus(retryTask.getStatus().getCode()); retryTaskPO.setArgsStr(retryTask.getArgsStr()); retryTaskPO.setGmtCreate(retryTask.getGmtCreate()); retryTaskPO.setGmtModified(retryTask.getGmtModified()); return retryTaskPO; } } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/main/java/com/alibaba/easyretry/extension/mybatis/common/utils/HostUtils.java ================================================ package com.alibaba.easyretry.extension.mybatis.common.utils; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Objects; public class HostUtils { private static final String IP; static { InetAddress address = null; try { address = InetAddress.getLocalHost(); } catch (UnknownHostException e) { // do noting } if (Objects.isNull(address)) { IP = "UNKNOW-IP"; } else { IP = address.getHostAddress(); } } public static String getHostIP() { return IP; } } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/main/java/com/alibaba/easyretry/extension/mybatis/dao/BaseDAOSupport.java ================================================ package com.alibaba.easyretry.extension.mybatis.dao; import java.util.Objects; import java.util.function.Function; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; /** * @author wuhao */ public abstract class BaseDAOSupport { private final SqlSessionFactory sqlSessionFactory; public BaseDAOSupport(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } protected T execute(Function function) { Objects.requireNonNull(sqlSessionFactory, "require sqlSessionFactory non null"); try (final SqlSession session = sqlSessionFactory.openSession(false)) { return function.apply(session); } } protected T execute(Function function, boolean autoCommit) { Objects.requireNonNull(sqlSessionFactory, "require sqlSessionFactory non null"); try (final SqlSession session = sqlSessionFactory.openSession(autoCommit)) { return function.apply(session); } } } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/main/java/com/alibaba/easyretry/extension/mybatis/dao/RetryTaskDAO.java ================================================ package com.alibaba.easyretry.extension.mybatis.dao; import java.util.List; import com.alibaba.easyretry.extension.mybatis.po.RetryTaskPO; import com.alibaba.easyretry.extension.mybatis.query.RetryTaskQuery; /** * @author Created by wuhao on 2020/11/8. */ public interface RetryTaskDAO { boolean saveRetryTask(RetryTaskPO retryTaskPO); List listRetryTask(RetryTaskQuery retryTaskQuery); boolean updateRetryTask(RetryTaskPO retryTaskPO); boolean deleteRetryTask(RetryTaskPO retryTaskPO); } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/main/java/com/alibaba/easyretry/extension/mybatis/dao/RetryTaskDAOImpl.java ================================================ package com.alibaba.easyretry.extension.mybatis.dao; import java.util.List; import com.alibaba.easyretry.extension.mybatis.po.RetryTaskPO; import com.alibaba.easyretry.extension.mybatis.query.RetryTaskQuery; import java.util.List; import org.apache.ibatis.session.SqlSessionFactory; /** * @author Created by wuhao on 2020/11/8. */ public class RetryTaskDAOImpl extends BaseDAOSupport implements RetryTaskDAO { public RetryTaskDAOImpl(SqlSessionFactory sqlSessionFactory) { super(sqlSessionFactory); } @Override public boolean saveRetryTask(RetryTaskPO retryTaskPO) { return execute( sqlSession -> sqlSession.insert("com.alibaba.easyretry.extension.mybatis.dao.RetryTaskDAO.saveRetryTask", retryTaskPO) > 0 , true); } @Override public List listRetryTask(RetryTaskQuery retryTaskQuery) { return execute(sqlSession -> sqlSession.selectList( "com.alibaba.easyretry.extension.mybatis.dao.RetryTaskDAO.listRetryTask", retryTaskQuery) ); } @Override public boolean updateRetryTask(RetryTaskPO retryTaskPO) { return execute(sqlSession -> sqlSession.update( "com.alibaba.easyretry.extension.mybatis.dao.RetryTaskDAO.updateRetryTask", retryTaskPO) > 0 , true); } @Override public boolean deleteRetryTask(RetryTaskPO retryTaskPO) { return execute(sqlSession -> sqlSession.delete( "com.alibaba.easyretry.extension.mybatis.dao.RetryTaskDAO.deleteRetryTask", retryTaskPO) > 0 , true); } } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/main/java/com/alibaba/easyretry/extension/mybatis/po/RetryTaskPO.java ================================================ package com.alibaba.easyretry.extension.mybatis.po; import java.util.Date; import com.alibaba.easyretry.common.constant.enums.RetryTaskStatusEnum; import lombok.Data; import lombok.experimental.Accessors; /** * @author Created by wuhao on 2020/11/8. */ @Data @Accessors(chain = true) public class RetryTaskPO { /** * 主键id */ private Long id; /** * 分片id */ private String sharding; /** * 业务信息 */ private String bizId; /** * 执行者名称 */ private String executorName; /** * 执行者方法 */ private String executorMethodName; /** * @see RetryTaskStatusEnum */ private Integer retryStatus; private String argsStr; private Date gmtCreate; private Date gmtModified; private String extAttrs; } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/main/java/com/alibaba/easyretry/extension/mybatis/query/RetryTaskQuery.java ================================================ package com.alibaba.easyretry.extension.mybatis.query; import java.util.List; import lombok.Data; import lombok.experimental.Accessors; /** * @author Created by wuhao on 2020/11/8. */ @Data @Accessors(chain = true) public class RetryTaskQuery { private Long lastId; private List retryStatus; private String sharding; } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/main/resources/dal/easyretry/easy-mybatis-config.xml ================================================ ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/main/resources/dal/easyretry/mapper/easy-retry-task-mapper.xml ================================================ `id` , `sharding`, `biz_id`, `executor_name`, `executor_method_name`, `retry_status`, `args_str`, `gmt_create`, `gmt_modified`, `ext_attrs` INSERT INTO easy_retry_task (`id`, `sharding`, `biz_id`, `executor_name`, `executor_method_name`, `retry_status`, `args_str`, `gmt_create`, `gmt_modified`, `ext_attrs`) VALUES (#{id}, #{sharding}, #{bizId}, #{executorName}, #{executorMethodName}, #{retryStatus}, #{argsStr}, #{gmtCreate}, #{gmtModified}, #{extAttrs}) UPDATE easy_retry_task SET gmt_modified = now() ,retry_status = #{retryStatus} WHERE id=#{id} DELETE FROM easy_retry_task WHERE id = #{id} ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/test/java/com/alibaba/easyretry/extension/mybatis/DbConfig.java ================================================ package com.alibaba.easyretry.extension.mybatis; import javax.sql.DataSource; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import lombok.Getter; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public final class DbConfig { @Getter private static final DataSource dataSource; static { String url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;INIT=runscript from 'classpath:/task.sql'"; HikariConfig config = new HikariConfig(); config.setJdbcUrl(url); config.setUsername("sa"); config.setDriverClassName("org.h2.Driver"); config.setPassword("sa"); config.addDataSourceProperty("cachePrepStmts", "true"); config.addDataSourceProperty("prepStmtCacheSize", "250"); config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); dataSource = new HikariDataSource(config); } @Test void test() { Assertions.assertNotNull(DbConfig.getDataSource()); } } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/test/java/com/alibaba/easyretry/extension/mybatis/MyBatisConfig.java ================================================ package com.alibaba.easyretry.extension.mybatis; import java.io.IOException; import lombok.Getter; import org.apache.ibatis.builder.xml.XMLConfigBuilder; import org.apache.ibatis.io.Resources; import org.apache.ibatis.logging.slf4j.Slf4jImpl; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public final class MyBatisConfig { @Getter private static final SqlSessionFactory factory; static { String resource = "dal/easyretry/easy-mybatis-config.xml"; Configuration parse; try { XMLConfigBuilder parser = new XMLConfigBuilder(Resources.getResourceAsStream(resource), null, null); parse = parser.parse(); } catch (IOException e) { throw new RuntimeException(e); } final Environment development = new Environment("development", new JdbcTransactionFactory(), DbConfig.getDataSource()); parse.setEnvironment(development); parse.setLogImpl(Slf4jImpl.class); factory = new SqlSessionFactoryBuilder().build(parse); } @Test void testNotNull() { Assertions.assertNotNull(MyBatisConfig.getFactory()); } } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/test/java/com/alibaba/easyretry/extension/mybatis/access/MybatisRetryTaskAccessTest.java ================================================ package com.alibaba.easyretry.extension.mybatis.access; import java.util.Date; import java.util.List; import java.util.Objects; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import com.alibaba.easyretry.common.constant.enums.RetryTaskStatusEnum; import com.alibaba.easyretry.common.entity.RetryTask; import com.alibaba.easyretry.extension.mybatis.MyBatisConfig; import com.alibaba.easyretry.extension.mybatis.dao.RetryTaskDAOImpl; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class MybatisRetryTaskAccessTest { private static final MybatisRetryTaskAccess ACCESS = new MybatisRetryTaskAccess( new RetryTaskDAOImpl( MyBatisConfig.getFactory())); private static RetryTask task; @BeforeAll static void prepare() { RetryTask retryTask = new RetryTask(); retryTask.setId(2L); retryTask.setGmtCreate(new Date()); retryTask.setGmtModified(new Date()); retryTask.setBizId("2"); retryTask.setStatus(RetryTaskStatusEnum.INIT); task = retryTask; } @Test @Order(1) void saveTask() { Assertions.assertTrue(ACCESS.saveRetryTask(task)); } @Test @Order(2) void handle() { // ACCESS.saveRetryTask(task); Assertions.assertTrue(ACCESS.handlingRetryTask(task)); List retryTasks = ACCESS.listAvailableTasks(1L); Assertions.assertTrue(Objects.nonNull(retryTasks) && !retryTasks.isEmpty()); Assertions.assertEquals(retryTasks.get(0).getStatus(), RetryTaskStatusEnum.HANDLING); } @Test @Order(3) void stop() { // ACCESS.saveRetryTask(task); boolean b = ACCESS.stopRetryTask(task); Assertions.assertTrue(b); List retryTasks = ACCESS.listAvailableTasks(1L); Assertions.assertTrue(Objects.isNull(retryTasks) || retryTasks.isEmpty()); } @Test @Order(4) void finish(){ // ACCESS.saveRetryTask(task); Assertions.assertTrue(ACCESS.finishRetryTask(task)); List retryTasks = ACCESS.listAvailableTasks(1L); Assertions.assertTrue(Objects.isNull(retryTasks) || retryTasks.isEmpty()); } } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/test/java/com/alibaba/easyretry/extension/mybatis/common/utils/HostUtilsTest.java ================================================ package com.alibaba.easyretry.extension.mybatis.common.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; class HostUtilsTest { @Test void getHostIP() { String hostIP = HostUtils.getHostIP(); assertNotNull(hostIP); } } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/test/java/com/alibaba/easyretry/extension/mybatis/dao/RetryTaskDAOImplTest.java ================================================ package com.alibaba.easyretry.extension.mybatis.dao; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Objects; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import com.alibaba.easyretry.common.constant.enums.RetryTaskStatusEnum; import com.alibaba.easyretry.extension.mybatis.MyBatisConfig; import com.alibaba.easyretry.extension.mybatis.po.RetryTaskPO; import com.alibaba.easyretry.extension.mybatis.query.RetryTaskQuery; class RetryTaskDAOImplTest { private static RetryTaskDAO retryTaskDAO; @BeforeAll static void prepare() { retryTaskDAO = new RetryTaskDAOImpl(MyBatisConfig.getFactory()); } @Test @Order(1) void saveTask() { final RetryTaskPO retryTaskPO = new RetryTaskPO() .setId(1L) .setGmtCreate(new Date()) .setBizId("1") .setRetryStatus(RetryTaskStatusEnum.HANDLING.getCode()) .setGmtModified(new Date()); final boolean result = retryTaskDAO.saveRetryTask(retryTaskPO); Assertions.assertTrue(result); } @Test @Order(2) void listRetryTask() { final RetryTaskPO retryTaskPO = new RetryTaskPO() .setId(2L) .setGmtCreate(new Date()) .setBizId("2") .setRetryStatus(RetryTaskStatusEnum.HANDLING.getCode()) .setGmtModified(new Date()); retryTaskDAO.saveRetryTask(retryTaskPO); final RetryTaskQuery retryTaskQuery = new RetryTaskQuery() .setRetryStatus(Collections.singletonList(RetryTaskStatusEnum.HANDLING.getCode())); List retryTaskPOS = retryTaskDAO.listRetryTask(retryTaskQuery); System.out.println(retryTaskPOS); Assertions.assertTrue(!retryTaskPOS.isEmpty()); Assertions.assertTrue(Objects.nonNull(retryTaskPOS)); // Assertions.assertTrue(Objects.nonNull(retryTaskPOS) && !retryTaskPOS.isEmpty()); } @Test @Order(3) void updateRetryTask() { //final RetryTaskPO retryTaskPO = new RetryTaskPO() // .setId(1L) // .setBizId("1") // .setRetryStatus(RetryTaskStatusEnum.FINISH.getCode()) // .setGmtModified(new Date()); // //boolean b = retryTaskDAO.updateRetryTask(retryTaskPO); //Assertions.assertTrue(b); // //final RetryTaskQuery retryTaskQuery = new RetryTaskQuery() // .setRetryStatus(Collections.singletonList(RetryTaskStatusEnum.FINISH.getCode())); //List retryTaskPOS = retryTaskDAO.listRetryTask(retryTaskQuery); //System.out.println(retryTaskPOS); //Assertions.assertTrue(Objects.nonNull(retryTaskPOS) && !retryTaskPOS.isEmpty()); } @Test @Order(4) void deleteRetryTask() { final RetryTaskPO retryTaskPO = new RetryTaskPO() .setId(3L) .setGmtCreate(new Date()) .setBizId("3") .setRetryStatus(RetryTaskStatusEnum.HANDLING.getCode()) .setGmtModified(new Date()); retryTaskDAO.saveRetryTask(retryTaskPO); boolean b = retryTaskDAO.deleteRetryTask(retryTaskPO); Assertions.assertTrue(b); final RetryTaskQuery retryTaskQuery = new RetryTaskQuery() .setRetryStatus(Collections.singletonList(RetryTaskStatusEnum.FINISH.getCode())); List retryTaskPOS = retryTaskDAO.listRetryTask(retryTaskQuery); System.out.println(retryTaskPOS); Assertions.assertTrue(Objects.isNull(retryTaskPOS) || retryTaskPOS.isEmpty()); } } ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/test/resources/logback.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: easy-retry-extensions/easy-retry-mybatis-extension/src/test/resources/task.sql ================================================ CREATE TABLE IF NOT EXISTS easy_retry_task ( id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键' PRIMARY KEY, gmt_create datetime NOT NULL COMMENT '创建时间', gmt_modified datetime NOT NULL COMMENT '修改时间', sharding varchar(64) NULL COMMENT '数据库分片字段', biz_id varchar(64) NULL COMMENT '业务id', executor_name varchar(512) NULL COMMENT '执行名称', executor_method_name varchar(512) NULL COMMENT '执行方法名称', retry_status tinyint NOT NULL COMMENT '重试状态', args_str varchar(3000) NULL COMMENT '执行方法参数', ext_attrs varchar(3000) NULL COMMENT '扩展字段' ); ================================================ FILE: easy-retry-extensions/easy-retry-spring-extension/pom.xml ================================================ 4.0.0 com.alibaba easy-retry-extensions ${revision} ../pom.xml easy-retry-spring-extension easy-retry-spring-extension easy-retry-spring-extension ${project.groupId} easy-retry-common ${project.groupId} easy-retry-core com.google.guava guava org.springframework spring-context 5.1.12.RELEASE org.springframework spring-core 5.1.12.RELEASE org.aspectj aspectjweaver 1.9.5 ================================================ FILE: easy-retry-extensions/easy-retry-spring-extension/src/main/java/com/alibaba/easyretry/extension/spring/RetryListenerInitialize.java ================================================ package com.alibaba.easyretry.extension.spring; import java.util.Map; import com.alibaba.easyretry.common.event.RetryEventMulticaster; import com.alibaba.easyretry.common.event.RetryListener; import lombok.Setter; import org.apache.commons.collections4.MapUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * @author Created by wuhao on 2021/4/9. */ public class RetryListenerInitialize implements SmartInitializingSingleton, ApplicationContextAware { @Setter private RetryEventMulticaster retryEventMulticaster; private ApplicationContext applicationContext; @Override public void afterSingletonsInstantiated() { Map retryListenerMap = applicationContext.getBeansOfType(RetryListener.class); MapUtils.emptyIfNull(retryListenerMap).values().forEach( (retryListener) -> retryEventMulticaster.register(retryListener)); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: easy-retry-extensions/easy-retry-spring-extension/src/main/java/com/alibaba/easyretry/extension/spring/SPELParamPredicate.java ================================================ package com.alibaba.easyretry.extension.spring; import java.lang.reflect.Method; import com.alibaba.easyretry.common.EasyRetryPredicate; import lombok.Data; import org.apache.commons.lang3.ArrayUtils; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; /** * @author Created by wuhao on 2021/3/18. */ @Data public class SPELParamPredicate implements EasyRetryPredicate { private static DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer(); private String bizIdCondition; private Method method; public SPELParamPredicate(String bizIdCondition, Method method) { this.bizIdCondition = bizIdCondition; this.method = method; } public SPELParamPredicate() { } @Override public String apply(Object[] params) { ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); String[] paramNameArr = discoverer.getParameterNames(method); if (ArrayUtils.isEmpty(paramNameArr)) { return null; } for (int i = 0; i < paramNameArr.length; i++) { context.setVariable(paramNameArr[i], params[i]); } return parser.parseExpression(bizIdCondition).getValue(context, String.class); } } ================================================ FILE: easy-retry-extensions/easy-retry-spring-extension/src/main/java/com/alibaba/easyretry/extension/spring/SPELResultPredicate.java ================================================ package com.alibaba.easyretry.extension.spring; import com.alibaba.easyretry.common.AbstractResultPredicate; import lombok.Data; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; /** * @author Created by wuhao on 2021/3/18. */ @Data public class SPELResultPredicate extends AbstractResultPredicate { private String resultCondition; public SPELResultPredicate(String resultCondition) { this.resultCondition = resultCondition; } public SPELResultPredicate() { } @Override public Boolean apply(T result) { ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("result", result); return parser.parseExpression(resultCondition).getValue(context, Boolean.class); } } ================================================ FILE: easy-retry-extensions/easy-retry-spring-extension/src/main/java/com/alibaba/easyretry/extension/spring/SpringEventApplicationListener.java ================================================ package com.alibaba.easyretry.extension.spring; import org.springframework.beans.factory.SmartInitializingSingleton; import com.alibaba.easyretry.common.filter.RetryFilterInvocationHandler; import com.alibaba.easyretry.common.filter.RetryFilterRegisterHandler; import lombok.Setter; /** * @author Created by wuhao on 2021/4/9. */ public class SpringEventApplicationListener implements SmartInitializingSingleton { @Setter private RetryFilterInvocationHandler retryFilterInvocationHandler; @Setter private RetryFilterRegisterHandler retryFilterRegisterHandler; @Override public void afterSingletonsInstantiated() { retryFilterRegisterHandler.handle(); retryFilterInvocationHandler.handle(); } } ================================================ FILE: easy-retry-extensions/easy-retry-spring-extension/src/main/java/com/alibaba/easyretry/extension/spring/SpringRetryFilterDiscover.java ================================================ package com.alibaba.easyretry.extension.spring; import java.util.List; import java.util.Map; import com.alibaba.easyretry.common.filter.RetryFilter; import com.alibaba.easyretry.common.filter.RetryFilterDiscover; import com.google.common.collect.Lists; import org.springframework.beans.BeansException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * @author Created by wuhao on 2021/4/9. */ public class SpringRetryFilterDiscover implements RetryFilterDiscover, SmartInitializingSingleton, ApplicationContextAware { private List retryFilters; private ApplicationContext applicationContext; @Override public List discoverAll() { return retryFilters; } @Override public void afterSingletonsInstantiated() { Map retryFilterMap = applicationContext.getBeansOfType(RetryFilter.class); retryFilters = Lists.newArrayList(retryFilterMap.values()); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: easy-retry-extensions/easy-retry-spring-extension/src/main/java/com/alibaba/easyretry/extension/spring/aop/EasyRetryable.java ================================================ package com.alibaba.easyretry.extension.spring.aop; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.alibaba.easyretry.common.constant.enums.RetryTypeEnum; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface EasyRetryable { /** * 处理完成以后是否需要把异常重新抛出 * * @return 是否需要抛出异 */ boolean reThrowException() default false; /** * 通过结果判断是否重试 */ String resultCondition() default ""; /** * 重试种类 */ RetryTypeEnum retryType() default RetryTypeEnum.ASYNC; } ================================================ FILE: easy-retry-extensions/easy-retry-spring-extension/src/main/java/com/alibaba/easyretry/extension/spring/aop/RetryInterceptor.java ================================================ package com.alibaba.easyretry.extension.spring.aop; import java.lang.reflect.Method; import java.util.Objects; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.RetryIdentify; import com.alibaba.easyretry.common.retryer.Retryer; import com.alibaba.easyretry.core.RetryerBuilder; import com.alibaba.easyretry.extension.spring.SPELResultPredicate; import lombok.Setter; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.context.ApplicationContext; @Aspect public class RetryInterceptor { @Setter private RetryConfiguration retryConfiguration; @Setter private ApplicationContext applicationContext; @Around("@annotation(retryable)") public Object around(ProceedingJoinPoint invocation, EasyRetryable retryable) throws Throwable { if (RetryIdentify.isOnRetry()) { return invocation.proceed(); } Retryer retryer = determineTargetRetryer(invocation, retryable); return retryer.call(invocation::proceed); } private String getBeanId(Class type) { String[] names = applicationContext.getBeanNamesForType(type); return names.length > 0 ? names[0] : null; } private Retryer determineTargetRetryer(ProceedingJoinPoint invocation, EasyRetryable retryable) { MethodSignature signature = (MethodSignature)invocation.getSignature(); RetryerBuilder retryerBuilder = new RetryerBuilder() .withExecutorName(getBeanId(signature.getDeclaringType())) .withExecutorMethodName(signature.getMethod().getName()) .withArgs(invocation.getArgs()) .withConfiguration(retryConfiguration) .withReThrowException(retryable.reThrowException()); if (StringUtils.isNotBlank(retryable.resultCondition())) { retryerBuilder.withResultPredicate(new SPELResultPredicate<>(retryable.resultCondition())); } return retryerBuilder.build(retryable.retryType()); } } ================================================ FILE: easy-retry-extensions/pom.xml ================================================ 4.0.0 com.alibaba easy-retry ${revision} ../pom.xml easy-retry-extensions pom easy-retry-extensions easy-retry-extensions easy-retry-spring-extension easy-retry-mybatis-extension easy-retry-guava-extension ================================================ FILE: easy-retry-starters/easy-retry-memory-starter/pom.xml ================================================ 4.0.0 com.alibaba easy-retry-starters ${revision} ../pom.xml easy-retry-memory-starter easy-retry-memory-starter easy-retry-memory-starter org.springframework.boot spring-boot-autoconfigure ${project.groupId} easy-retry-spring-extension ${project.groupId} easy-retry-core ${project.groupId} easy-retry-starter-common ================================================ FILE: easy-retry-starters/easy-retry-memory-starter/src/main/java/com/alibaba/easyretry/memory/MemoryAutoConfiguration.java ================================================ package com.alibaba.easyretry.memory; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.RetryContainer; import com.alibaba.easyretry.common.RetryExecutor; import com.alibaba.easyretry.core.access.MemoryRetryTaskAccess; import com.alibaba.easyretry.core.container.SimpleRetryContainer; import com.alibaba.easyretry.memory.config.EasyRetryMemoryCompatibleProperties; import com.alibaba.easyretry.starter.common.CommonAutoConfiguration; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author Created by wuhao on 2021/2/19. */ @Configuration @Slf4j @EnableConfigurationProperties(EasyRetryMemoryCompatibleProperties.class) @ConditionalOnProperty(name = "spring.easyretry.memory.enabled", havingValue = "true") public class MemoryAutoConfiguration extends CommonAutoConfiguration { @Autowired private EasyRetryMemoryCompatibleProperties easyRetryMemoryCompatibleProperties; @Bean @ConditionalOnMissingBean(MemoryRetryTaskAccess.class) public MemoryRetryTaskAccess retryTaskAccess() { return new MemoryRetryTaskAccess(); } @Bean(initMethod = "start") public RetryContainer retryContainer( RetryConfiguration configuration, RetryExecutor defaultRetryExecutor) { log.warn("RetryConfiguration start"); return new SimpleRetryContainer( configuration, defaultRetryExecutor); } @Override public Integer getMaxRetryTimes() { return easyRetryMemoryCompatibleProperties.getMaxRetryTimes(); } } ================================================ FILE: easy-retry-starters/easy-retry-memory-starter/src/main/java/com/alibaba/easyretry/memory/config/EasyRetryMemoryCompatibleProperties.java ================================================ package com.alibaba.easyretry.memory.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; /** * @author Created by wuhao on 2021/2/21. */ @ConfigurationProperties(prefix = "spring.easyretry.memory") @Data public class EasyRetryMemoryCompatibleProperties { private Integer maxRetryTimes = 5; private String namespace = "easy-retry"; } ================================================ FILE: easy-retry-starters/easy-retry-memory-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json ================================================ { "properties": [ { "name": "spring.easyretry.memory.enabled", "type": "java.lang.Boolean", "description": "enable AmoebaCompatible or not." }, { "name": "spring.easyretry.memory.maxRetryTimes", "type": "java.lang.Integer", "description": "definition the maxRetryTimes" } ] } ================================================ FILE: easy-retry-starters/easy-retry-memory-starter/src/main/resources/META-INF/spring.factories ================================================ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.alibaba.easyretry.memory.MemoryAutoConfiguration ================================================ FILE: easy-retry-starters/easy-retry-mybatis-starter/pom.xml ================================================ 4.0.0 com.alibaba easy-retry-starters ${revision} ../pom.xml easy-retry-mybatis-starter easy-retry-mybatis-starter easy-retry-mybatis-starter org.springframework.boot spring-boot-autoconfigure com.alibaba easy-retry-spring-extension ${project.groupId} easy-retry-core ${project.groupId} easy-retry-mybatis-extension org.mybatis mybatis-spring 2.0.6 org.springframework spring-tx 5.1.12.RELEASE ${project.groupId} easy-retry-starter-common ================================================ FILE: easy-retry-starters/easy-retry-mybatis-starter/src/main/java/com/alibaba/easyretry/mybatis/MybatisAutoConfiguration.java ================================================ package com.alibaba.easyretry.mybatis; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.RetryContainer; import com.alibaba.easyretry.common.RetryExecutor; import com.alibaba.easyretry.common.access.RetryTaskAccess; import com.alibaba.easyretry.core.container.SimpleRetryContainer; import com.alibaba.easyretry.extension.mybatis.access.MybatisRetryTaskAccess; import com.alibaba.easyretry.extension.mybatis.dao.RetryTaskDAO; import com.alibaba.easyretry.extension.mybatis.dao.RetryTaskDAOImpl; import com.alibaba.easyretry.mybatis.conifg.EasyRetryMybatisProperties; import com.alibaba.easyretry.starter.common.CommonAutoConfiguration; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; import javax.sql.DataSource; /** * @author Created by wuhao on 2021/2/19. */ @Configuration @Slf4j @EnableConfigurationProperties(EasyRetryMybatisProperties.class) @ConditionalOnProperty(name = "spring.easyretry.mybatis.enabled", matchIfMissing = true) public class MybatisAutoConfiguration extends CommonAutoConfiguration { @Autowired private EasyRetryMybatisProperties easyRetryMybatisProperties; @Value("classpath:/dal/easyretry/easy-mybatis-config.xml") private Resource easyRetryMybatisResouse; @Bean("easyRetrySqlSessionFactory") public SqlSessionFactory sqlSessionFactory( @Qualifier("easyRetryMybatisDataSource") DataSource easyRetryMybatisDataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(easyRetryMybatisDataSource); sqlSessionFactoryBean.setConfigLocation(easyRetryMybatisResouse); return sqlSessionFactoryBean.getObject(); } @Bean(initMethod = "start") public RetryContainer retryContainer( RetryConfiguration configuration, RetryExecutor defaultRetryExecutor) { log.warn("RetryConfiguration start"); return new SimpleRetryContainer( configuration, defaultRetryExecutor); } @Bean public RetryTaskDAO retryTaskDAO( @Qualifier("easyRetrySqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new RetryTaskDAOImpl(sqlSessionFactory); } @Bean public RetryTaskAccess retryTaskAccess(RetryTaskDAO retryTaskDAO) { return new MybatisRetryTaskAccess(retryTaskDAO); } @Override public Integer getMaxRetryTimes() { return easyRetryMybatisProperties.getMaxRetryTimes(); } } ================================================ FILE: easy-retry-starters/easy-retry-mybatis-starter/src/main/java/com/alibaba/easyretry/mybatis/conifg/EasyRetryMybatisProperties.java ================================================ package com.alibaba.easyretry.mybatis.conifg; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; /** * @author Created by wuhao on 2021/2/21. */ @ConfigurationProperties(prefix = "spring.easyretry.mybatis") @Data public class EasyRetryMybatisProperties { private Integer maxRetryTimes = 5; } ================================================ FILE: easy-retry-starters/easy-retry-mybatis-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json ================================================ { "properties": [ { "name": "spring.easyretry.mybatis.enabled", "type": "java.lang.Boolean", "description": "enable AmoebaCompatible or not." }, { "name": "spring.easyretry.mybatis.maxRetryTimes", "type": "java.lang.Integer", "description": "definition the maxRetryTimes" } ] } ================================================ FILE: easy-retry-starters/easy-retry-mybatis-starter/src/main/resources/META-INF/spring.factories ================================================ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.alibaba.easyretry.mybatis.MybatisAutoConfiguration ================================================ FILE: easy-retry-starters/easy-retry-starter-common/pom.xml ================================================ 4.0.0 com.alibaba easy-retry-starters ${revision} ../pom.xml easy-retry-starter-common easy-retry-starter-common easy-retry-starter-common org.springframework.boot spring-boot-autoconfigure com.alibaba easy-retry-spring-extension com.alibaba easy-retry-guava-extension ${project.groupId} easy-retry-core ================================================ FILE: easy-retry-starters/easy-retry-starter-common/src/main/java/com/alibaba/easyretry/starter/common/CommonAutoConfiguration.java ================================================ package com.alibaba.easyretry.starter.common; import org.springframework.beans.BeansException; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import com.alibaba.easyretry.common.RetryConfiguration; import com.alibaba.easyretry.common.RetryExecutor; import com.alibaba.easyretry.common.access.RetrySerializerAccess; import com.alibaba.easyretry.common.access.RetryStrategyAccess; import com.alibaba.easyretry.common.access.RetryTaskAccess; import com.alibaba.easyretry.common.event.RetryEventMulticaster; import com.alibaba.easyretry.common.filter.RetryFilterDiscover; import com.alibaba.easyretry.common.filter.RetryFilterInvocation; import com.alibaba.easyretry.common.filter.RetryFilterInvocationHandler; import com.alibaba.easyretry.common.filter.RetryFilterRegister; import com.alibaba.easyretry.common.filter.RetryFilterRegisterHandler; import com.alibaba.easyretry.common.resolve.ExecutorSolver; import com.alibaba.easyretry.common.serializer.ResultPredicateSerializer; import com.alibaba.easyretry.common.strategy.StopStrategy; import com.alibaba.easyretry.common.strategy.WaitStrategy; import com.alibaba.easyretry.core.PersistenceRetryExecutor; import com.alibaba.easyretry.core.access.DefaultRetrySerializerAccess; import com.alibaba.easyretry.core.event.SimpleRetryEventMulticaster; import com.alibaba.easyretry.core.filter.DefaultRetryFilterInvocationHandler; import com.alibaba.easyretry.core.filter.DefaultRetryFilterRegisterHandler; import com.alibaba.easyretry.core.filter.SimpleRetryFilterRegister; import com.alibaba.easyretry.core.serializer.HessianResultPredicateSerializer; import com.alibaba.easyretry.core.strategy.DefaultRetryStrategy; import com.alibaba.easyretry.extension.spring.RetryListenerInitialize; import com.alibaba.easyretry.extension.spring.SpringEventApplicationListener; import com.alibaba.easyretry.extension.spring.SpringRetryFilterDiscover; import com.alibaba.easyretry.extension.spring.aop.RetryInterceptor; import lombok.extern.slf4j.Slf4j; /** * @author Created by wuhao on 2021/2/19. */ @Slf4j public abstract class CommonAutoConfiguration implements ApplicationContextAware { protected ApplicationContext applicationContext; @Bean @ConditionalOnMissingBean(RetryConfiguration.class) public RetryConfiguration configuration(RetryTaskAccess retryTaskAccess, RetryEventMulticaster retryEventMulticaster) { DefaultRetryStrategy defaultRetryStrategy = new DefaultRetryStrategy(); return new RetryConfiguration() { @Override public RetryTaskAccess getRetryTaskAccess() { return retryTaskAccess; } @Override public RetrySerializerAccess getRetrySerializerAccess() { return new DefaultRetrySerializerAccess(); } @Override public RetryStrategyAccess getRetryStrategyAccess() { return new RetryStrategyAccess() { @Override public StopStrategy getCurrentGlobalStopStrategy() { return defaultRetryStrategy; } @Override public WaitStrategy getCurrentGlobalWaitStrategy() { return defaultRetryStrategy; } }; } @Override public ExecutorSolver getExecutorSolver() { return executorName -> applicationContext.getBean(executorName); } @Override public ResultPredicateSerializer getResultPredicateSerializer() { return new HessianResultPredicateSerializer(); } @Override public Integer getMaxRetryTimes() { return CommonAutoConfiguration.this.getMaxRetryTimes(); } @Override public RetryEventMulticaster getRetryEventMulticaster() { return retryEventMulticaster; } }; } @Bean @ConditionalOnMissingBean(RetryInterceptor.class) public RetryInterceptor retryInterceptor(RetryConfiguration configuration) { RetryInterceptor retryInterceptor = new RetryInterceptor(); retryInterceptor.setApplicationContext(applicationContext); retryInterceptor.setRetryConfiguration(configuration); return retryInterceptor; } @Bean @ConditionalOnMissingBean(RetryExecutor.class) public RetryExecutor defaultRetryExecutor(RetryConfiguration configuration, RetryFilterInvocation retryInvocationHandler) { PersistenceRetryExecutor persistenceRetryExecutor = new PersistenceRetryExecutor(); persistenceRetryExecutor.setRetryConfiguration(configuration); persistenceRetryExecutor.setRetryFilterInvocation(retryInvocationHandler); return persistenceRetryExecutor; } @Bean @ConditionalOnMissingBean(RetryEventMulticaster.class) public RetryEventMulticaster retryEventMulticaster() { return new SimpleRetryEventMulticaster(); } @Bean @ConditionalOnMissingBean(RetryListenerInitialize.class) public RetryListenerInitialize retryListenerInitialize(RetryEventMulticaster retryEventMulticaster) { RetryListenerInitialize retryListenerInitialize = new RetryListenerInitialize(); retryListenerInitialize.setRetryEventMulticaster(retryEventMulticaster); return retryListenerInitialize; } @Bean @ConditionalOnMissingBean(SpringRetryFilterDiscover.class) public SpringRetryFilterDiscover springRetryFilterDiscover() { return new SpringRetryFilterDiscover(); } @Bean @ConditionalOnMissingBean(RetryFilterRegister.class) public SimpleRetryFilterRegister simpleRetryFilterRegister() { return new SimpleRetryFilterRegister(); } @Bean @ConditionalOnMissingBean(RetryFilterInvocationHandler.class) public DefaultRetryFilterInvocationHandler retryInvocationHandler(RetryFilterRegister simpleRetryFilterRegister) { DefaultRetryFilterInvocationHandler defaultRetryFilterInvocationHandler = new DefaultRetryFilterInvocationHandler(); defaultRetryFilterInvocationHandler.setRetryFilterRegister(simpleRetryFilterRegister); return defaultRetryFilterInvocationHandler; } @Bean @ConditionalOnMissingBean(RetryFilterRegisterHandler.class) public RetryFilterRegisterHandler retryFilterRegisterHandler(RetryFilterDiscover springRetryFilterDiscover, RetryFilterRegister simpleRetryFilterRegister) { DefaultRetryFilterRegisterHandler defaultRetryFilterRegisterHandler = new DefaultRetryFilterRegisterHandler(); defaultRetryFilterRegisterHandler.setRetryFilterRegister(simpleRetryFilterRegister); defaultRetryFilterRegisterHandler.setRetryFilterDiscover(springRetryFilterDiscover); return defaultRetryFilterRegisterHandler; } @Bean public SpringEventApplicationListener easyRetryApplicationListener(RetryFilterInvocationHandler retryFilterInvocationHandler, RetryFilterRegisterHandler retryFilterRegisterHandler) { SpringEventApplicationListener springEventApplicationListener = new SpringEventApplicationListener(); springEventApplicationListener.setRetryFilterRegisterHandler(retryFilterRegisterHandler); springEventApplicationListener.setRetryFilterInvocationHandler(retryFilterInvocationHandler); return springEventApplicationListener; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public abstract Integer getMaxRetryTimes(); } ================================================ FILE: easy-retry-starters/pom.xml ================================================ 4.0.0 com.alibaba easy-retry ${revision} ../pom.xml easy-retry-starters pom easy-retry-starters easy-retry-starters easy-retry-memory-starter easy-retry-mybatis-starter easy-retry-starter-common org.springframework.boot spring-boot-autoconfigure 2.4.0 ================================================ FILE: pom.xml ================================================ 4.0.0 com.alibaba easy-retry ${revision} pom 1.8 1.8 1.8 UTF-8 UTF-8 1.0.3 1.7.30 1.18.18 16.0 1.2.73 2.2.1 3.2.0 1.6 easy-retry-common easy-retry-core easy-retry-extensions easy-retry-starters easy-retry easy-retry https://github.com/alibaba/easy-retry https://github.com/alibaba/easy-retry scm:git@github.com:alibaba/easy-retry.git Alibaba Group https://github.com/alibaba wuhao wuhao7171@gmail.com MIT License http://www.opensource.org/licenses/mit-license.php org.junit.jupiter junit-jupiter test org.junit junit-bom 5.7.1 pom import org.slf4j slf4j-api ${slf4j.version} org.projectlombok lombok ${lombok.version} true com.google.guava guava ${guava.version} com.alibaba easy-retry-core ${project.version} com.alibaba easy-retry-common ${project.version} com.caucho hessian 4.0.60 provided org.apache.commons commons-lang3 3.11 com.alibaba fastjson ${fastjson.version} org.apache.commons commons-collections4 4.4 ${project.groupId} easy-retry-spring-extension ${project.version} ${project.groupId} easy-retry-guava-extension ${project.version} ${project.groupId} easy-retry-mybatis-extension ${project.version} ${project.groupId} easy-retry-starter-common ${project.version} org.apache.maven.plugins maven-compiler-plugin 3.8.1 1.8 1.8 UTF-8 org.apache.maven.plugins maven-resources-plugin 3.2.0 UTF-8 org.codehaus.mojo flatten-maven-plugin 1.1.0 true oss flatten process-resources flatten flatten.clean clean clean org.apache.maven.plugins maven-surefire-plugin 3.0.0-M5 release org.apache.maven.plugins maven-gpg-plugin ${maven-gpg-plugin.version} verify sign org.apache.maven.plugins maven-source-plugin 3.2.1 attach-sources jar org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc-plugin.version} attach-javadocs jar esnapshots https://oss.sonatype.org/content/repositories/snapshots erelease https://oss.sonatype.org/service/local/staging/deploy/maven2/