commit 47c9fb863dbd3c811294a5abf03975ae947d4867
Author: byteblogs168 <598092184@qq.com>
Date: Sat Sep 2 17:24:47 2023 +0800
easy retry demo 初始化
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8f237d5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+/build/
+
+### VS Code ###
+.vscode/
+
+.flattened-pom.xml
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..36a80aa
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,13 @@
+FROM openjdk:8-jdk-alpine
+MAINTAINER www.byteblogs.com
+
+ADD ./target/easy-retry-example.jar easy-retry-example.jar
+
+#对外暴漏的端口号
+EXPOSE 8018
+
+WORKDIR /
+
+#开机启动
+ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /easy-retry-example.jar $PARAMS"]
+
diff --git a/docs/demo.sql b/docs/demo.sql
new file mode 100644
index 0000000..7aef6ef
--- /dev/null
+++ b/docs/demo.sql
@@ -0,0 +1,15 @@
+DROP DATABASE IF EXISTS demo;
+CREATE DATABASE demo;
+USE demo;
+
+CREATE TABLE `demo`.`fail_order` (
+ `id` bigint NOT NULL COMMENT '自增主键Id',
+ `orderId` bigint NOT NULL COMMENT '订单Id',
+ `sourceId` int NOT NULL COMMENT '来源Id',
+ `sceneName` varchar(255) NOT NULL COMMENT '场景名称',
+ `executorName` varchar(255) NOT NULL COMMENT '执行器名称',
+ `args` varchar(255) NULL COMMENT '参数信息',
+ `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
+ PRIMARY KEY (`id`)
+ );
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..b854008
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,111 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.6.8
+
+
+
+ com.example
+ example
+ 1.0.0
+ example
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ com.aizuda
+ easy-retry-client-starter
+ 2.2.0
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ 3.5.1
+
+
+ com.baomidou
+ mybatis-plus-generator
+ 3.5.1
+
+
+ org.freemarker
+ freemarker
+ 2.3.28
+ compile
+
+
+ cn.hutool
+ hutool-all
+ 5.8.19
+
+
+ mysql
+ mysql-connector-java
+ 8.0.30
+
+
+
+ io.springfox
+ springfox-boot-starter
+ 3.0.0
+
+
+
+ com.alibaba
+ transmittable-thread-local
+ 2.12.0
+
+
+ org.awaitility
+ awaitility
+ 4.1.1
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.2.0
+
+
+
+
+ easy-retry-example
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/example/easy/retry/EasyRetrySpringbootApplication.java b/src/main/java/com/example/easy/retry/EasyRetrySpringbootApplication.java
new file mode 100644
index 0000000..f062672
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/EasyRetrySpringbootApplication.java
@@ -0,0 +1,17 @@
+package com.example.easy.retry;
+
+import com.aizuda.easy.retry.client.starter.EnableEasyRetry;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+@EnableEasyRetry(group = "easy_retry_demo_group")
+@MapperScan("com.example.easy.retry.dao")
+public class EasyRetrySpringbootApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(EasyRetrySpringbootApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/com/example/easy/retry/config/SwaggerConfig.java b/src/main/java/com/example/easy/retry/config/SwaggerConfig.java
new file mode 100644
index 0000000..128c8c2
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/config/SwaggerConfig.java
@@ -0,0 +1,51 @@
+package com.example.easy.retry.config;
+
+import com.aizuda.easy.retry.common.core.util.EasyRetryVersion;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * @author: www.byteblogs.com
+ * @date : 2023-07-17 18:19
+ * @since 2.1.0
+ */
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+ @Bean
+ public Docket api() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .select()
+ .apis(RequestHandlerSelectors.basePackage("com.example.easy.retry.controller")) // 替换为你的项目包名
+ .paths(PathSelectors.any())
+ .build()
+ .apiInfo(apiInfo());
+ }
+
+ private ApiInfo apiInfo() {
+ return new ApiInfoBuilder()
+ .title("Easy Retry Example")
+ .description(
+ "
EasyRetry是基于BASE思想实现的分布式服务重试组件
\n" +
+ "官网地址: https://www.easyretry.com/
" +
+ "在线体验地址: http://preview.easyretry.com/
"+
+ "源码地址: https://gitee.com/byteblogs168/easy-retry-demo
" +
+ "特别提醒: 🌻在您使用测试案例之前请认真的阅读官网.
"
+ )
+ .version(EasyRetryVersion.getVersion())
+ .build();
+ }
+}
+
+
+
+
+
+
diff --git a/src/main/java/com/example/easy/retry/controller/LocalAndRemoteRetryController.java b/src/main/java/com/example/easy/retry/controller/LocalAndRemoteRetryController.java
new file mode 100644
index 0000000..13e2bf9
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/controller/LocalAndRemoteRetryController.java
@@ -0,0 +1,42 @@
+package com.example.easy.retry.controller;
+
+import com.example.easy.retry.service.LocalRemoteService;
+import com.example.easy.retry.service.LocalRetryService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.UUID;
+
+@RestController
+@RequestMapping("/local-remote")
+@Api(value = "模拟先本地再远程重试案例", tags = "先本地再远程重试案例【RetryType.LOCAL_REMOTE】")
+public class LocalAndRemoteRetryController {
+
+ @Autowired
+ private LocalRemoteService localRemoteService;
+
+ @GetMapping("/retry")
+ @ApiOperation(value = "一个简单的入门案例")
+ public void localRemote() {
+ localRemoteService.localRemote();
+ }
+
+ @GetMapping("/retryWithLocalRemote")
+ @ApiOperation(
+ value = "使用同步上报的方式",
+ notes = "async = false 代表使用同步上传的方式\n"
+ + "timeout = 1 代表超时时间为1 \n"
+ + "unit = MINUTES 代表超时时间的单位是分钟\n" +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void remoteRetryWithLocalRemote(@ApiParam(name = "params", value = "测试参数", defaultValue = "test")
+ @RequestParam("params") String params) {
+ localRemoteService.remoteRetryWithLocalRemote(params);
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/controller/LocalRetryController.java b/src/main/java/com/example/easy/retry/controller/LocalRetryController.java
new file mode 100644
index 0000000..2f0768d
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/controller/LocalRetryController.java
@@ -0,0 +1,86 @@
+package com.example.easy.retry.controller;
+
+import io.swagger.annotations.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.example.easy.retry.service.LocalRetryService;
+
+@RestController
+@RequestMapping("/local")
+@Api(value = "模拟本地重试", tags = "本地重试案例 【RetryType.ONLY_LOCAL】")
+public class LocalRetryController {
+
+ @Autowired
+ private LocalRetryService localRetryService;
+
+ @GetMapping("/retry")
+ @ApiOperation(
+ value = "一个简单的入门案例",
+ notes = "🥇仅仅在本地进行内存重试\n" +
+ "📢任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void onlyLocalRetry(@ApiParam(name = "params", value = "测试参数", defaultValue = "test") @RequestParam("params") String params){
+ localRetryService.localRetry(params);
+ }
+
+ @GetMapping("/withBasicParams")
+ @ApiOperation(
+ value = "指定基础参数",
+ notes = "localTimes 本地重试次数\n" +
+ "localInterval 本地重试间隔时间(默认单位为秒)\n" +
+ "unit 超时时间单位\n" +
+ "本案例设置为本地重试4次,每次重试之间间隔10s"
+ )
+ public void localRetryWithBasicParams(@ApiParam(name = "params", value = "测试参数", defaultValue = "test") @RequestParam("params") String params){
+ localRetryService.localRetryWithBasicParams(params);
+ }
+
+ @GetMapping("/includeException")
+ @ApiOperation(
+ value = "指定异常参数",
+ notes = "include参数指的是当我们遭遇到指定异常时进行重试\n" +
+ "在这个案例中我们处理两个场景:\n" +
+ "抛出指定异常,例如抛出自定义的ParamException异常,观察是否会重试\n" +
+ "抛出非指定异常,例如我们在这里产生一个异常,观察是否会重试\n" +
+ "注意:如果此时我们在include 中指定参数为BusinessException(ParamException的父类),同样也会进行重试逻辑\n" +
+ "下面参数可以指定:NullPointerException, ParamException"
+ )
+ @ApiImplicitParams({
+ @ApiImplicitParam(name = "type", value = "异常类型", dataType = "String", paramType = "query",
+ allowableValues = "NullPointerException,ParamException", defaultValue = "ParamException")
+ })
+ public void localRetryIncludeException(@RequestParam("type") String type){
+ localRetryService.localRetryIncludeException(type);
+ }
+
+ @GetMapping("/excludeException")
+ @ApiOperation(
+ value = "非指定异常参数进行重试",
+ notes = "这个参数的作用和include是相反的\n" +
+ "exclude参数指的是当我们遇到指定异常时则不会进行重试\n" +
+ "比如在下述案例中我们指定了遇到ParamException和ArithmeticException后不进行重试"
+ )
+ @ApiImplicitParams({
+ @ApiImplicitParam(name = "type", value = "异常类型", dataType = "String", paramType = "query",
+ allowableValues = "ParamException,ArithmeticException", defaultValue = "ParamException")
+ })
+ public void localRetryExcludeException(@RequestParam("type") String type){
+ localRetryService.localRetryExcludeException(type);
+ }
+
+ @GetMapping("/isThrowException")
+ @ApiOperation(
+ value = "本地重试完成后不抛出异常",
+ notes = ""
+ )
+ @ApiImplicitParams({
+ @ApiImplicitParam(name = "params", value = "异常类型", dataType = "String", paramType = "query")
+ })
+ public void localRetryIsThrowException(@RequestParam("params") String params){
+ localRetryService.localRetryIsThrowException(params);
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/controller/ManualRetryExecutorController.java b/src/main/java/com/example/easy/retry/controller/ManualRetryExecutorController.java
new file mode 100644
index 0000000..73cd2d4
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/controller/ManualRetryExecutorController.java
@@ -0,0 +1,30 @@
+package com.example.easy.retry.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.example.easy.retry.service.ManualRetryExecutorMethodService;
+
+@RestController
+@RequestMapping("/manual")
+@Api(value = "模拟手动执行重试案例", tags = "手动执行重试上报")
+public class ManualRetryExecutorController {
+
+ @Autowired
+ private ManualRetryExecutorMethodService manualRetryExecutorMethodService;
+ @ApiOperation(
+ value = "手动重试",
+ notes = "❤️如果不知道这个手动重试的使用场景可以参考: https://www.easyretry.com/pages/406a68/#%E5%8F%91%E9%80%81mq%E5%9C%BA%E6%99%AF \n"
+ + "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ @GetMapping("/retry")
+ public void remoteRetryWithCallback(@ApiParam(name = "params", value = "测试参数", defaultValue = "test") @RequestParam("params") String params){
+ manualRetryExecutorMethodService.myExecutorMethod(params);
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/controller/RemoteRetryController.java b/src/main/java/com/example/easy/retry/controller/RemoteRetryController.java
new file mode 100644
index 0000000..cf3cb5e
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/controller/RemoteRetryController.java
@@ -0,0 +1,165 @@
+package com.example.easy.retry.controller;
+
+
+import java.util.Random;
+import java.util.UUID;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.example.easy.retry.vo.OrderVo;
+import com.example.easy.retry.service.RemoteRetryService;
+
+@RestController
+@RequestMapping("/remote")
+@Api(value = "模拟远程重试案例", tags = "远程重试案例【RetryType.ONLY_REMOTE】")
+public class RemoteRetryController {
+
+ @Autowired
+ private RemoteRetryService remoteRetryService;
+
+ /**
+ * 一个最简单的远程调用案例
+ */
+ @GetMapping("/retry")
+ @ApiOperation(
+ value = "一个简单的入门案例",
+ notes = "🥇不进过本地重试阶段,直接上报到服务端\n" +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void remote(@ApiParam(name = "params", value = "测试参数", defaultValue = "test")
+ @RequestParam("params") String params) {
+ remoteRetryService.remoteRetry(params);
+ }
+
+ /**
+ * 一个最简单的远程调用案例
+ */
+ @GetMapping("/retry/sync")
+ @ApiOperation(
+ value = "一个简单的以同步方式远程重试入门案例",
+ notes = "🥇不进过本地重试阶段,直接上报到服务端\n" +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void remoteSync(@ApiParam(name = "params", value = "测试参数", defaultValue = "test")
+ @RequestParam("params") String params) {
+ remoteRetryService.remoteSync(params);
+ }
+
+ /**
+ * 使用自定义的幂等Id生成规则
+ */
+ @PostMapping("/retryWithIdempotentId")
+ @ApiOperation(
+ value = "使用自定义的幂等Id生成规则",
+ notes =
+ "具体实现类参考: https://gitee.com/zhangyutongxue/easy-retry-demo/blob/master/easy-retry-springboot/src/main/java/com/maluxinyu/easyretry/customized/OrderIdempotentIdGenerate.java\n"
+ + "具体的幂等策略参考: https://www.easyretry.com/pages/97cde9/#%E2%9A%A1%E5%B9%82%E7%AD%89id-idempotentid \n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void remoteRetryWithIdempotentId(@RequestBody OrderVo orderVo) {
+ remoteRetryService.remoteRetryWithIdempotentId(orderVo);
+ }
+
+ /**
+ * 使用自定义的单参数幂等Id生成规则
+ */
+ @ApiOperation(
+ value = "使用自定义的单参数幂等Id生成规则",
+ notes =
+ "具体实现类参考: https://gitee.com/zhangyutongxue/easy-retry-demo/blob/master/easy-retry-springboot/src/main/java/com/maluxinyu/easyretry/customized/SingleParamIdempotentGenerate.java\n"
+ +
+ "🥇通过对orderId进行md5加密生成幂等ID, 具体的幂等策略参考: https://www.easyretry.com/pages/97cde9/#%E2%9A%A1%E5%B9%82%E7%AD%89id-idempotentid \n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ @GetMapping("/retryWithSingleParamIdempotentGenerate")
+ public void retryWithSingleParamIdempotentGenerate(
+ @ApiParam(name = "params", value = "测试参数", defaultValue = "test")
+ @RequestParam("params") String params) {
+ remoteRetryService.retryWithSingleParamIdempotentGenerate(params);
+ }
+
+ /**
+ * 使用自定义的多参数幂等Id生成规则
+ */
+ @PostMapping("/retryWithMulParamIdempotentGenerate")
+ @ApiOperation(
+ value = "使用自定义的多参数幂等Id生成规则",
+ notes =
+ "具体实现类参考: https://gitee.com/zhangyutongxue/easy-retry-demo/blob/master/easy-retry-springboot/src/main/java/com/maluxinyu/easyretry/customized/MultiParamIdempotentGenerate.java\n"
+ +
+ "🥇通过对orderId进行md5加密生成幂等ID, 具体的幂等策略参考: https://www.easyretry.com/pages/97cde9/#%E2%9A%A1%E5%B9%82%E7%AD%89id-idempotentid \n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ public void retryWithMulParamIdempotentGenerate(@RequestBody OrderVo orderVo) {
+ Random random = new Random();
+ remoteRetryService.retryWithMulParamIdempotentGenerate(
+ String.valueOf(UUID.randomUUID()),
+ random.nextInt(),
+ random.nextDouble(),
+ 'a',
+ orderVo
+ );
+ }
+
+ /**
+ * 使用自定义的异常处理类 OrderRetryMethod
+ */
+ @ApiOperation(
+ value = "指定自定义的异常处理类",
+ notes =
+ "具体实现类参考: https://gitee.com/zhangyutongxue/easy-retry-demo/blob/master/easy-retry-springboot/src/main/java/com/maluxinyu/easyretry/customized/OrderRetryMethod.java\n"
+ +
+ "🥇什么是自定义的异常处理类: https://www.easyretry.com/pages/540554/#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%E5%99%A8\n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ @PostMapping("/retryWithRetryMethod")
+ public void remoteRetryWithRetryMethod(@RequestBody OrderVo orderVo) {
+ remoteRetryService.remoteRetryWithRetryMethod(orderVo);
+ }
+
+ /**
+ * 使用自定义的回调函数
+ */
+ @ApiOperation(
+ value = "使用自定义的回调函数",
+ notes =
+ "具体实现类参考: https://gitee.com/zhangyutongxue/easy-retry-demo/blob/master/easy-retry-springboot/src/main/java/com/maluxinyu/easyretry/customized/OrderCompleteCallback.java\n"
+ +
+ "🥇什么情况下触发回调: https://www.easyretry.com/pages/97cde9/#%E2%9A%A1%E5%9B%9E%E8%B0%83\n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ @PostMapping("/retryWithCallback")
+ public void remoteRetryWithCallback(@RequestBody OrderVo orderVo) {
+ remoteRetryService.remoteRetryWithCompleteCallback(orderVo);
+ }
+
+ /**
+ * 指定bizNo
+ */
+ @ApiOperation(
+ value = "指定bizNo",
+ notes = "🥇什么是bizNo: https://www.easyretry.com/pages/540554/#bizno%E7%94%9F%E6%88%90%E5%99%A8\n"
+ +
+ "📢查看任务列表: http://preview.easyretry.com/#/retry-task/list"
+ )
+ @PostMapping("/remoteRetryWithBizNo")
+ public void remoteRetryWithBizNo(@RequestBody OrderVo orderVo) {
+ remoteRetryService.remoteRetryWithBizNo(orderVo);
+ }
+
+
+}
diff --git a/src/main/java/com/example/easy/retry/customized/MultiParamIdempotentGenerate.java b/src/main/java/com/example/easy/retry/customized/MultiParamIdempotentGenerate.java
new file mode 100644
index 0000000..4df8b22
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/customized/MultiParamIdempotentGenerate.java
@@ -0,0 +1,25 @@
+package com.example.easy.retry.customized;
+
+import com.aizuda.easy.retry.client.core.IdempotentIdGenerate;
+import com.aizuda.easy.retry.common.core.model.IdempotentIdContext;
+import com.example.easy.retry.vo.OrderVo;
+
+import cn.hutool.crypto.SecureUtil;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class MultiParamIdempotentGenerate implements IdempotentIdGenerate {
+
+ @Override
+ public String idGenerate(IdempotentIdContext idempotentIdContext) throws Exception {
+ Object[] args = idempotentIdContext.getArgs();
+ String uuid = (String) args[0];
+ Integer intVal = (Integer) args[1];
+ Double doubleVal = (Double) args[2];
+ Character character = (Character) args[3];
+ OrderVo orderVo = (OrderVo) args[4];
+ log.info("测试多参数解析,String类型:{},Integer类型:{},Double类型:{}," +
+ "Character类型:{},对象类型:{}",uuid,intVal,doubleVal,character,orderVo);
+ return SecureUtil.md5(orderVo.getOrderId());
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/customized/OrderCompleteCallback.java b/src/main/java/com/example/easy/retry/customized/OrderCompleteCallback.java
new file mode 100644
index 0000000..cbf6367
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/customized/OrderCompleteCallback.java
@@ -0,0 +1,56 @@
+package com.example.easy.retry.customized;
+
+import cn.hutool.json.JSONUtil;
+import com.aizuda.easy.retry.client.core.callback.RetryCompleteCallback;
+import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
+import com.example.easy.retry.dao.FailOrderBaseMapper;
+import com.example.easy.retry.po.FailOrderPo;
+import com.example.easy.retry.vo.OrderVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class OrderCompleteCallback implements RetryCompleteCallback {
+
+ @Autowired
+ private FailOrderBaseMapper failOrderBaseMapper;
+
+ /**
+ * 重试成功后的回调函数
+ * 参数1-场景名称
+ * 参数2-执行器名称
+ * 参数3-入参信息
+ */
+ @Override
+ public void doSuccessCallback(String sceneName, String executorName, Object[] objects) {
+ // 重试成功后删除失败表中的数据
+ OrderVo orderVo = (OrderVo) objects[0];
+ log.info("远程重试成功,场景{},执行器{},参数信息",sceneName,executorName, JSONUtil.toJsonStr(objects));
+ failOrderBaseMapper.delete(
+ new LambdaQueryChainWrapper<>(failOrderBaseMapper)
+ .eq(FailOrderPo::getOrderId,orderVo.getOrderId())
+ );
+ }
+
+ /**
+ * 重试达到最大次数后的回调函数
+ * 参数1-场景名称
+ * 参数2-执行器名称
+ * 参数3-入参信息
+ */
+ @Override
+ public void doMaxRetryCallback(String sceneName, String executorName, Object[] objects) {
+ OrderVo orderVo = (OrderVo) objects[0];
+ log.info("远程重试达到最大限度,场景{},执行器{},参数信息",sceneName,executorName, JSONUtil.toJsonStr(objects));
+ // 重试失败后插入订单失败信息
+ failOrderBaseMapper.insert(FailOrderPo.builder()
+ .orderId(orderVo.getOrderId())
+ .sourceId(orderVo.getSource())
+ .sceneName(sceneName)
+ .executorName(executorName)
+ .args(JSONUtil.toJsonStr(objects))
+ .build());
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/customized/OrderIdempotentIdGenerate.java b/src/main/java/com/example/easy/retry/customized/OrderIdempotentIdGenerate.java
new file mode 100644
index 0000000..c08db6b
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/customized/OrderIdempotentIdGenerate.java
@@ -0,0 +1,21 @@
+package com.example.easy.retry.customized;
+
+import org.apache.catalina.security.SecurityUtil;
+
+import com.aizuda.easy.retry.client.core.IdempotentIdGenerate;
+import com.aizuda.easy.retry.common.core.model.IdempotentIdContext;
+import com.example.easy.retry.vo.OrderVo;
+
+import cn.hutool.crypto.SecureUtil;
+
+
+public class OrderIdempotentIdGenerate implements IdempotentIdGenerate {
+
+ @Override
+ public String idGenerate(IdempotentIdContext idempotentIdContext) throws Exception {
+ Object[] args = idempotentIdContext.getArgs();
+ OrderVo orderVo = (OrderVo) args[0];
+ return SecureUtil.md5(orderVo.getOrderId());
+ }
+
+}
diff --git a/src/main/java/com/example/easy/retry/customized/OrderRetryMethod.java b/src/main/java/com/example/easy/retry/customized/OrderRetryMethod.java
new file mode 100644
index 0000000..158be30
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/customized/OrderRetryMethod.java
@@ -0,0 +1,24 @@
+package com.example.easy.retry.customized;
+
+import java.lang.reflect.Array;
+
+import org.springframework.stereotype.Component;
+
+import com.aizuda.easy.retry.client.core.strategy.ExecutorMethod;
+import com.example.easy.retry.vo.OrderVo;
+
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class OrderRetryMethod implements ExecutorMethod {
+ @Override
+ public Object doExecute(Object params) {
+ // 将特定类型的 Object 对象指定为 Object[]
+ Object[] args = (Object[]) params;
+ OrderVo orderVo = (OrderVo) args[0];
+ log.info("进入指定自定义的异常处理类, 参数信息是{}", JSONUtil.toJsonStr(orderVo));
+ throw new ArithmeticException("自定义的异常处理类处理");
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/customized/SingleParamIdempotentGenerate.java b/src/main/java/com/example/easy/retry/customized/SingleParamIdempotentGenerate.java
new file mode 100644
index 0000000..a2f1dfd
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/customized/SingleParamIdempotentGenerate.java
@@ -0,0 +1,17 @@
+package com.example.easy.retry.customized;
+
+import com.aizuda.easy.retry.client.core.IdempotentIdGenerate;
+import com.aizuda.easy.retry.common.core.model.IdempotentIdContext;
+import com.example.easy.retry.vo.OrderVo;
+
+import cn.hutool.crypto.SecureUtil;
+
+public class SingleParamIdempotentGenerate implements IdempotentIdGenerate {
+
+ @Override
+ public String idGenerate(IdempotentIdContext idempotentIdContext) throws Exception {
+ Object[] args = idempotentIdContext.getArgs();
+ String params = (String) args[0];
+ return SecureUtil.md5(params);
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/dao/FailOrderBaseMapper.java b/src/main/java/com/example/easy/retry/dao/FailOrderBaseMapper.java
new file mode 100644
index 0000000..ed4095f
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/dao/FailOrderBaseMapper.java
@@ -0,0 +1,11 @@
+package com.example.easy.retry.dao;
+
+import org.springframework.stereotype.Repository;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.example.easy.retry.po.FailOrderPo;
+
+@Repository
+public interface FailOrderBaseMapper extends BaseMapper {
+
+}
diff --git a/src/main/java/com/example/easy/retry/exception/BusinessException.java b/src/main/java/com/example/easy/retry/exception/BusinessException.java
new file mode 100644
index 0000000..e40dff0
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/exception/BusinessException.java
@@ -0,0 +1,12 @@
+package com.example.easy.retry.exception;
+
+/**
+ * 业务异常类
+ */
+public class BusinessException extends RuntimeException{
+
+ public BusinessException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/com/example/easy/retry/exception/ParamException.java b/src/main/java/com/example/easy/retry/exception/ParamException.java
new file mode 100644
index 0000000..2d93671
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/exception/ParamException.java
@@ -0,0 +1,10 @@
+package com.example.easy.retry.exception;
+/**
+ * 参数异常处理类
+ */
+public class ParamException extends BusinessException{
+ public ParamException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/com/example/easy/retry/executor/ManualRetryExecutorTask.java b/src/main/java/com/example/easy/retry/executor/ManualRetryExecutorTask.java
new file mode 100644
index 0000000..8057729
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/executor/ManualRetryExecutorTask.java
@@ -0,0 +1,26 @@
+package com.example.easy.retry.executor;
+
+import com.aizuda.easy.retry.client.core.annotation.ExecutorMethodRegister;
+import com.aizuda.easy.retry.client.core.strategy.ExecutorMethod;
+import com.example.easy.retry.vo.OrderVo;
+
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+
+@ExecutorMethodRegister(scene = ManualRetryExecutorTask.SCENE)
+@Slf4j
+public class ManualRetryExecutorTask implements ExecutorMethod {
+ /**
+ * 自定义场景值
+ */
+ public final static String SCENE = "manualRetry";
+
+ @Override
+ public Object doExecute(Object params) {
+ // 将特定类型的 Object 对象指定为 Object[]
+ Object[] args = (Object[]) params;
+ OrderVo orderVo = JSONUtil.toBean(JSONUtil.toJsonStr(args[0]), OrderVo.class);
+ log.info("进入手动重试方法,参数信息是{}", JSONUtil.toJsonStr(orderVo));
+ return true;
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/po/FailOrderPo.java b/src/main/java/com/example/easy/retry/po/FailOrderPo.java
new file mode 100644
index 0000000..ec730c6
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/po/FailOrderPo.java
@@ -0,0 +1,41 @@
+package com.example.easy.retry.po;
+
+import java.time.LocalDateTime;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 处理失败的订单信息表
+ */
+@TableName("fail_order")
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class FailOrderPo {
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+ // 订单ID
+ private String orderId;
+ // 来源ID
+ private Integer sourceId;
+ // 场景名称
+ private String sceneName;
+ // 执行器名称
+ private String executorName;
+ // 参数信息
+ private String args;
+ // 创建时间
+ private LocalDateTime createDate;
+ // 更新时间
+ private LocalDateTime updateDate;
+
+}
diff --git a/src/main/java/com/example/easy/retry/service/LocalRemoteService.java b/src/main/java/com/example/easy/retry/service/LocalRemoteService.java
new file mode 100644
index 0000000..f4fe7fa
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/service/LocalRemoteService.java
@@ -0,0 +1,36 @@
+package com.example.easy.retry.service;
+
+import com.aizuda.easy.retry.client.core.annotation.Retryable;
+import com.aizuda.easy.retry.client.core.retryer.RetryType;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 模拟先本地再远程重试案例
+ *
+ * @author www.byteblogs.com
+ * @date 2023-07-18 22:19:30
+ * @since 2.1.0
+ */
+@Service
+public class LocalRemoteService {
+
+ @Retryable(scene = "localRemote", retryStrategy = RetryType.LOCAL_REMOTE)
+ public void localRemote() {
+ System.out.println("local retry 方法开始执行");
+ double i = 1 / 0;
+ }
+
+ /**
+ * 使用先本地再远程的策略同步上传重试请求 retryStrategy = LOCAL_REMOTE 代表本地重试3次后再执行远程上报 async = false 代表使用同步上传的方式 timeout = 1 代表超时时间为1
+ * unit = MINUTES 代表超时时间的单位是分钟
+ */
+ @Retryable(scene = "remoteRetryWithSync", retryStrategy = RetryType.LOCAL_REMOTE,
+ async = false, timeout = 1, unit = TimeUnit.MINUTES)
+ public String remoteRetryWithLocalRemote(String requestId) {
+ double i = 1 / 0;
+ return requestId;
+ }
+
+}
diff --git a/src/main/java/com/example/easy/retry/service/LocalRetryService.java b/src/main/java/com/example/easy/retry/service/LocalRetryService.java
new file mode 100644
index 0000000..8a5bd61
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/service/LocalRetryService.java
@@ -0,0 +1,85 @@
+package com.example.easy.retry.service;
+
+import com.aizuda.easy.retry.client.core.retryer.RetryType;
+import org.springframework.stereotype.Component;
+
+import com.aizuda.easy.retry.client.core.annotation.Retryable;
+import com.example.easy.retry.exception.ParamException;
+
+/**
+ * easy-retry中的本地重试demo
+ * 测试类入口见 {@link com.example.easy.retry.local.RetryableTest}
+ */
+
+@Component
+public class LocalRetryService {
+ /**
+ * 入门案例
+ * 我们仅仅需要指定场景值scene就可以给方法赋予重试逻辑
+ * 其他的参数均可以省略或者使用默认值
+ * [参数释义]
+ * 场景值scene对应我们后台的场景参数,用于后续的分组
+ * 在微服务中建议大家直接使用集群的服务名称即可
+ */
+ @Retryable(scene = "localRetry", retryStrategy = RetryType.ONLY_LOCAL)
+ public void localRetry(String params) {
+ System.out.println("local retry 方法开始执行");
+ double i = 1 / 0;
+ }
+
+ /**
+ * 指定基础参数
+ * localTimes 本地重试次数
+ * localInterval 本地重试间隔时间(默认单位为秒)
+ * unit 超时时间单位
+ * 以下案例指的是本地重试4次,每次重试之间间隔10s
+ */
+ @Retryable(scene = "localRetryWithBasicParams", localTimes = 4, localInterval = 10, retryStrategy = RetryType.ONLY_LOCAL)
+ public void localRetryWithBasicParams(String params) {
+ System.out.println("local retry with basic params 方法开始执行");
+ double i = 1 / 0;
+ }
+
+ /**
+ * 指定异常参数
+ * include参数指的是当我们遭遇到指定异常时进行重试
+ * 在这个案例中我们处理两个场景:
+ * 抛出指定异常,例如抛出自定义的ParamException异常,观察是否会重试
+ * 抛出非指定异常,例如我们在这里产生一个异常,观察是否会重试
+ * 注意:如果此时我们在include 中指定参数为BusinessException(ParamException的父类),同样也会进行重试逻辑
+ * 更多用法:如果我们需要在include中指定多个参数,可以使用 include = {ParamException.class,NullPointerException.class}
+ */
+ @Retryable(scene = "localRetryIncludeException", include = ParamException.class, retryStrategy = RetryType.ONLY_LOCAL)
+ public void localRetryIncludeException(String params) {
+ System.out.println("local retry include exception 方法开始执行");
+ if ("NullPointerException".equals(params)) {
+ throw new NullPointerException();
+ } else {
+ throw new ParamException("此处发生了指定异常Param Exception");
+ }
+ }
+
+ /**
+ * 这个参数的作用和include是相反的
+ * exclude参数指的是当我们遇到指定异常时则不会进行重试
+ * 比如在下述案例中我们指定了遇到ParamException和ArithmeticException后不进行重试
+ */
+ @Retryable(scene = "localRetryExcludeException", exclude = {ParamException.class, ArithmeticException.class}, retryStrategy = RetryType.ONLY_LOCAL)
+ public void localRetryExcludeException(String type) {
+ System.out.println("local retry exclude exception 方法开始执行");
+
+ if ("ParamException".equals(type)) {
+ throw new ParamException("此处发生了指定异常Param Exception");
+ } else if ("ArithmeticException".equals(type)) {
+ throw new ParamException("此处发生了指定异常Arithme Exception");
+ } else {
+ throw new UnsupportedOperationException("未知异常");
+ }
+ }
+
+ @Retryable(scene = "localRetryIsThrowException", isThrowException = false, retryStrategy = RetryType.ONLY_LOCAL)
+ public void localRetryIsThrowException(String params) {
+ System.out.println("local retry is throw exception 方法开始执行");
+ throw new ParamException("此处发生了参数异常");
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/service/ManualRetryExecutorMethodService.java b/src/main/java/com/example/easy/retry/service/ManualRetryExecutorMethodService.java
new file mode 100644
index 0000000..e669170
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/service/ManualRetryExecutorMethodService.java
@@ -0,0 +1,33 @@
+package com.example.easy.retry.service;
+
+import org.springframework.stereotype.Component;
+
+import com.aizuda.easy.retry.client.core.retryer.EasyRetryTemplate;
+import com.aizuda.easy.retry.client.core.retryer.RetryTaskTemplateBuilder;
+import com.example.easy.retry.executor.ManualRetryExecutorTask;
+import com.example.easy.retry.vo.OrderVo;
+
+/**
+ * easy-retry中的手动重试
+ */
+@Component
+public class ManualRetryExecutorMethodService {
+
+ public void myExecutorMethod(String params) {
+ OrderVo orderVo = OrderVo.builder()
+ .orderId(params)
+ .source(1)
+ .build();
+ EasyRetryTemplate easyRetryTemplate = RetryTaskTemplateBuilder.newBuilder()
+ // 手动指定场景名称
+ .withScene(ManualRetryExecutorTask.SCENE)
+ // 指定要执行的任务
+ .withExecutorMethod(ManualRetryExecutorTask.class)
+ // 指定参数
+ .withParam(orderVo)
+ .build();
+ // 执行模板
+ easyRetryTemplate.executeRetry();
+ }
+
+}
diff --git a/src/main/java/com/example/easy/retry/service/RemoteRetryService.java b/src/main/java/com/example/easy/retry/service/RemoteRetryService.java
new file mode 100644
index 0000000..e12af86
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/service/RemoteRetryService.java
@@ -0,0 +1,108 @@
+package com.example.easy.retry.service;
+
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.aizuda.easy.retry.client.core.annotation.Retryable;
+import com.aizuda.easy.retry.client.core.retryer.RetryType;
+import com.example.easy.retry.customized.MultiParamIdempotentGenerate;
+import com.example.easy.retry.customized.OrderIdempotentIdGenerate;
+import com.example.easy.retry.customized.OrderCompleteCallback;
+import com.example.easy.retry.customized.OrderRetryMethod;
+import com.example.easy.retry.customized.SingleParamIdempotentGenerate;
+import com.example.easy.retry.dao.FailOrderBaseMapper;
+import com.example.easy.retry.po.FailOrderPo;
+import com.example.easy.retry.vo.OrderVo;
+
+import cn.hutool.db.sql.Order;
+import cn.hutool.json.JSONUtil;
+
+/**
+ * easy-retry中的远程重试
+ */
+@Component
+public class RemoteRetryService {
+
+ /**
+ * 定义一个最基本的远程重试方法
+ */
+ @Retryable(scene = "remoteRetry", retryStrategy = RetryType.ONLY_REMOTE)
+ public void remoteRetry(String params) {
+ System.out.println("远程重试方法启动");
+ double i = 1 / 0;
+ }
+
+ @Retryable(scene = "remoteRetrySync", retryStrategy = RetryType.ONLY_REMOTE, async = false, timeout = 500, unit = TimeUnit.MILLISECONDS)
+ public void remoteSync(String params) {
+ System.out.println("同步远程重试方法启动");
+ double i = 1 / 0;
+ }
+
+ /**
+ * 使用自定义的幂等Id生成类 OrderIdempotentIdGenerate
+ */
+ @Retryable(scene = "remoteRetryWithIdempotentId", retryStrategy = RetryType.ONLY_REMOTE,
+ idempotentId = OrderIdempotentIdGenerate.class)
+ public boolean remoteRetryWithIdempotentId(OrderVo orderVo) {
+ double i = 1 / 0;
+ return true;
+ }
+
+ /**
+ * 使用自定义的幂等Id生成类 SingleParamIdempotentGenerate 测试单参数场景下
+ */
+ @Retryable(scene = "retryWithSingleParamIdempotentGenerate", retryStrategy = RetryType.ONLY_REMOTE,
+ idempotentId = SingleParamIdempotentGenerate.class)
+ public boolean retryWithSingleParamIdempotentGenerate(String params) {
+ throw new NullPointerException();
+ }
+
+ /**
+ * 使用自定义的幂等Id生成类 MultiParamIdempotentGenerate 测试多个参数场景下的解析
+ */
+ @Retryable(scene = "retryWithMulParamIdempotentGenerate", retryStrategy = RetryType.ONLY_REMOTE,
+ idempotentId = MultiParamIdempotentGenerate.class)
+ public boolean retryWithMulParamIdempotentGenerate(String uuid, Integer intVal, Double doubleVal,
+ Character character, OrderVo orderVo) {
+ throw new NullPointerException();
+ }
+
+ /**
+ * 使用自定义的异常处理类 OrderRetryMethod
+ */
+ @Retryable(scene = "remoteRetryWithRetryMethod", retryStrategy = RetryType.ONLY_REMOTE,
+ retryMethod = OrderRetryMethod.class)
+ public boolean remoteRetryWithRetryMethod(OrderVo orderVo) {
+ throw new NullPointerException();
+ }
+
+ /**
+ * 使用自定义的retryCompleteCallback类 OrderCompleteCallback
+ */
+ @Retryable(scene = "remoteRetryWithCompleteCallback", retryStrategy = RetryType.LOCAL_REMOTE,
+ retryCompleteCallback = OrderCompleteCallback.class)
+ public boolean remoteRetryWithCompleteCallback(OrderVo orderVo) {
+ Random random = new Random();
+ // 生成一个随机数,范围为0到1之间
+ double probability = random.nextDouble();
+ // 判断随机数是否小于等于0.5,即50%的概率
+ if (probability <= 0.5) {
+ // 生成的结果在50%的概率下执行这里的逻辑
+ throw new NullPointerException();
+ }
+ return true;
+ }
+
+ /**
+ * 自定义BizNo
+ */
+ @Retryable(scene = "remoteRetryWithBizNo", retryStrategy = RetryType.ONLY_REMOTE, bizNo = "#orderVo.orderId")
+ public boolean remoteRetryWithBizNo(OrderVo orderVo) {
+ throw new NullPointerException();
+ }
+
+
+}
diff --git a/src/main/java/com/example/easy/retry/spi/MyEasyRetryListener.java b/src/main/java/com/example/easy/retry/spi/MyEasyRetryListener.java
new file mode 100644
index 0000000..90fd09b
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/spi/MyEasyRetryListener.java
@@ -0,0 +1,30 @@
+package com.example.easy.retry.spi;
+
+import com.aizuda.easy.retry.client.core.event.EasyRetryListener;
+import com.aizuda.easy.retry.common.core.util.JsonUtil;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @author: www.byteblogs.com
+ * @date : 2023-08-28 11:20
+ */
+@Slf4j
+public class MyEasyRetryListener implements EasyRetryListener {
+
+ @Override
+ public void beforeRetry(final String sceneName, final String executorClassName, final Object[] params) {
+ log.info("------> beforeRetry sceneName:[{}] executorClassName:[{}] params:[{}]",
+ sceneName, executorClassName, JsonUtil.toJsonString(params));
+ }
+
+ @Override
+ public void successOnRetry(final Object result, final String sceneName, final String executorClassName) {
+ log.info("------> successOnRetry sceneName:[{}] executorClassName:[{}] result:[{}]",
+ sceneName, executorClassName, JsonUtil.toJsonString(result));
+ }
+
+ @Override
+ public void failureOnRetry(final String sceneName, final String executorClassName, final Throwable e) {
+ log.info("------> failureOnRetry sceneName:[{}] executorClassName:[{}]", sceneName, executorClassName, e);
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/spi/TTLRetrySiteSnapshotContext.java b/src/main/java/com/example/easy/retry/spi/TTLRetrySiteSnapshotContext.java
new file mode 100644
index 0000000..9627171
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/spi/TTLRetrySiteSnapshotContext.java
@@ -0,0 +1,32 @@
+package com.example.easy.retry.spi;
+
+import com.aizuda.easy.retry.client.core.RetrySiteSnapshotContext;
+import com.alibaba.ttl.TransmittableThreadLocal;
+
+/**
+ * @author: www.byteblogs.com
+ * @date : 2023-08-28 11:30
+ */
+public class TTLRetrySiteSnapshotContext implements RetrySiteSnapshotContext {
+
+ private final ThreadLocal threadLocal;
+
+ public TTLRetrySiteSnapshotContext() {
+ this.threadLocal = new TransmittableThreadLocal<>();
+ }
+
+ @Override
+ public void set(final T value) {
+ threadLocal.set(value);
+ }
+
+ @Override
+ public void remove() {
+ threadLocal.remove();
+ }
+
+ @Override
+ public T get() {
+ return threadLocal.get();
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/util/CodeGen.java b/src/main/java/com/example/easy/retry/util/CodeGen.java
new file mode 100644
index 0000000..f32fd3c
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/util/CodeGen.java
@@ -0,0 +1,52 @@
+package com.example.easy.retry.util;
+
+import org.springframework.beans.factory.annotation.Value;
+
+import com.baomidou.mybatisplus.generator.AutoGenerator;
+import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
+import com.baomidou.mybatisplus.generator.config.GlobalConfig;
+import com.baomidou.mybatisplus.generator.config.PackageConfig;
+import com.baomidou.mybatisplus.generator.config.StrategyConfig;
+
+public class CodeGen {
+
+ @Value("${spring.datasource.url}")
+ private static String dataSourceUrl;
+
+ @Value("${spring.datasource.username}")
+ private static String userName;
+
+ @Value("${spring.datasource.password}")
+ private static String password;
+
+ public static void main(String[] args) {
+ DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(dataSourceUrl, userName, password).build();
+
+ // 全局配置
+ GlobalConfig globalConfig = new GlobalConfig.Builder()
+ .outputDir("src/main/java")
+ .author("maluxinyu")
+ .build();
+
+ // 策略配置
+ StrategyConfig strategyConfig = new StrategyConfig.Builder()
+ .addInclude("fail_order") // 需要生成的表名
+ .build();
+
+ // 包配置
+ PackageConfig packageConfig = new PackageConfig.Builder()
+ .parent("com.maluxinyu.easyretry")
+ .moduleName("easy-retry-springboot")
+ .build();
+
+ // 代码生成器
+ AutoGenerator generator = new AutoGenerator(dataSourceConfig)
+ .global(globalConfig)
+ .strategy(strategyConfig)
+ .packageInfo(packageConfig);
+
+ // 执行生成代码
+ generator.execute();
+
+ }
+}
diff --git a/src/main/java/com/example/easy/retry/vo/OrderVo.java b/src/main/java/com/example/easy/retry/vo/OrderVo.java
new file mode 100644
index 0000000..9320692
--- /dev/null
+++ b/src/main/java/com/example/easy/retry/vo/OrderVo.java
@@ -0,0 +1,22 @@
+package com.example.easy.retry.vo;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class OrderVo {
+ @ApiModelProperty(value = "订单ID,用于唯一标识订单的编号", required = true)
+ private String orderId; // 订单ID,用于唯一标识订单的编号
+ @ApiModelProperty(value = "订单来源信息,1-手机端下单 2-PC端下单",required = true)
+ private Integer source; // 订单来源信息,1-手机端下单 2-PC端下单
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..7752cb6
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,44 @@
+server:
+ port: 8018
+spring:
+ mvc:
+ pathmatch:
+ matching-strategy: ant_path_matcher
+ profiles:
+ active: dev
+ datasource:
+ name: easy_retry
+ url: jdbc:mysql://localhost:3306/demo?useSSL=false&characterEncoding=utf8&useUnicode=true
+ username: root
+ password: root
+ type: com.zaxxer.hikari.HikariDataSource
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ hikari:
+ connection-timeout: 30000
+ minimum-idle: 5
+ maximum-pool-size: 20
+ auto-commit: true
+ idle-timeout: 30000
+ pool-name: demo
+ max-lifetime: 1800000
+ connection-test-query: SELECT 1
+mybatis-plus:
+ mapper-locations: classpath:/mapper/*.xml
+ typeAliasesPackage: com.example.easy.retry.po
+ global-config:
+ db-config:
+ field-strategy: NOT_EMPTY
+ capital-mode: false
+ logic-delete-value: 1
+ logic-not-delete-value: 0
+ configuration:
+ map-underscore-to-camel-case: true
+ cache-enabled: true
+
+logging:
+ config: classpath:logback-boot.xml
+
+easy-retry:
+ server:
+ host: 127.0.0.1
+ port: 1788
diff --git a/src/main/resources/logback-boot.xml b/src/main/resources/logback-boot.xml
new file mode 100644
index 0000000..ebb1737
--- /dev/null
+++ b/src/main/resources/logback-boot.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{256} - %msg%n
+
+
+
+
+
+ ./data/log/${log.base}/info/info.log
+
+ ./data/log/${log.base}/info/info.%d{yyyy-MM-dd}.log
+ 30
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{256} - %msg%n
+
+
+ INFO
+ ACCEPT
+ DENY
+
+
+
+
+ ./data/log/${log.base}/warn/warn.log
+
+ ./data/log/${log.base}/warn/warn.%d{yyyy-MM-dd}.log
+
+ 30
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{256} - %msg%n
+
+
+ WARN
+ ACCEPT
+ DENY
+
+
+
+
+ ./data/log/${log.base}/error/error.log
+
+ ./data/log/${log.base}/error/error.%d{yyyy-MM-dd}.log
+
+ 30
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{256} - %msg%n
+
+
+ ERROR
+ ACCEPT
+ DENY
+
+
+
+
+ 100
+ 1024
+
+
+
+
+ 100
+ 1024
+
+
+
+
+ 100
+ 1024
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/services/com.aizuda.easy.retry.client.core.RetryArgSerializer b/src/main/resources/services/com.aizuda.easy.retry.client.core.RetryArgSerializer
new file mode 100644
index 0000000..30c4ca4
--- /dev/null
+++ b/src/main/resources/services/com.aizuda.easy.retry.client.core.RetryArgSerializer
@@ -0,0 +1,2 @@
+#com.aizuda.easy.retry.client.core.serializer.HessianSerializer
+#com.aizuda.easy.retry.client.core.serializer.JacksonSerializer
diff --git a/src/main/resources/services/com.aizuda.easy.retry.client.core.RetrySiteSnapshotContext b/src/main/resources/services/com.aizuda.easy.retry.client.core.RetrySiteSnapshotContext
new file mode 100644
index 0000000..f8d0216
--- /dev/null
+++ b/src/main/resources/services/com.aizuda.easy.retry.client.core.RetrySiteSnapshotContext
@@ -0,0 +1 @@
+#com.maluxinyu.easyretry.spi.TTLRetrySiteSnapshotContext
diff --git a/src/main/resources/services/com.aizuda.easy.retry.client.core.event.EasyRetryListener b/src/main/resources/services/com.aizuda.easy.retry.client.core.event.EasyRetryListener
new file mode 100644
index 0000000..6837c1d
--- /dev/null
+++ b/src/main/resources/services/com.aizuda.easy.retry.client.core.event.EasyRetryListener
@@ -0,0 +1 @@
+#com.maluxinyu.easyretry.spi.MyEasyRetryListener