easy retry demo 初始化
This commit is contained in:
commit
47c9fb863d
31
.gitignore
vendored
Normal file
31
.gitignore
vendored
Normal file
@ -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
|
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@ -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"]
|
||||
|
15
docs/demo.sql
Normal file
15
docs/demo.sql
Normal file
@ -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`)
|
||||
);
|
111
pom.xml
Normal file
111
pom.xml
Normal file
@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.6.8</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>example</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<name>example</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aizuda</groupId>
|
||||
<artifactId>easy-retry-client-starter</artifactId>
|
||||
<version>2.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.5.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-generator</artifactId>
|
||||
<version>3.5.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
<version>2.3.28</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.19</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.30</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-boot-starter</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>transmittable-thread-local</artifactId>
|
||||
<version>2.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<version>4.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.2.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>easy-retry-example</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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(
|
||||
"<h1>EasyRetry是基于BASE思想实现的分布式服务重试组件</h1> \n" +
|
||||
"<h3>官网地址: https://www.easyretry.com/</h3>" +
|
||||
"<h3>在线体验地址: http://preview.easyretry.com/</h3> "+
|
||||
"<h3>源码地址: https://gitee.com/byteblogs168/easy-retry-demo</h3>" +
|
||||
"<h3>特别提醒: 🌻在您使用测试案例之前请认真的阅读官网.</h3>"
|
||||
)
|
||||
.version(EasyRetryVersion.getVersion())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
@ -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("自定义的异常处理类处理");
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<FailOrderPo> {
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.example.easy.retry.exception;
|
||||
|
||||
/**
|
||||
* 业务异常类
|
||||
*/
|
||||
public class BusinessException extends RuntimeException{
|
||||
|
||||
public BusinessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.example.easy.retry.exception;
|
||||
/**
|
||||
* 参数异常处理类
|
||||
*/
|
||||
public class ParamException extends BusinessException{
|
||||
public ParamException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
41
src/main/java/com/example/easy/retry/po/FailOrderPo.java
Normal file
41
src/main/java/com/example/easy/retry/po/FailOrderPo.java
Normal file
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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("此处发生了参数异常");
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<T> implements RetrySiteSnapshotContext<T> {
|
||||
|
||||
private final ThreadLocal<T> 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();
|
||||
}
|
||||
}
|
52
src/main/java/com/example/easy/retry/util/CodeGen.java
Normal file
52
src/main/java/com/example/easy/retry/util/CodeGen.java
Normal file
@ -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();
|
||||
|
||||
}
|
||||
}
|
22
src/main/java/com/example/easy/retry/vo/OrderVo.java
Normal file
22
src/main/java/com/example/easy/retry/vo/OrderVo.java
Normal file
@ -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端下单
|
||||
}
|
44
src/main/resources/application.yml
Normal file
44
src/main/resources/application.yml
Normal file
@ -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
|
88
src/main/resources/logback-boot.xml
Normal file
88
src/main/resources/logback-boot.xml
Normal file
@ -0,0 +1,88 @@
|
||||
<configuration>
|
||||
|
||||
<property name="log.base" value="easy-retry-demo" />
|
||||
|
||||
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{256} - %msg%n
|
||||
</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="fileInfo"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>./data/log/${log.base}/info/info.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<FileNamePattern>./data/log/${log.base}/info/info.%d{yyyy-MM-dd}.log</FileNamePattern>
|
||||
<MaxHistory>30</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{256} - %msg%n</pattern>
|
||||
</layout>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>INFO</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="fileWarn" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>./data/log/${log.base}/warn/warn.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<FileNamePattern>./data/log/${log.base}/warn/warn.%d{yyyy-MM-dd}.log
|
||||
</FileNamePattern>
|
||||
<MaxHistory>30</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{256} - %msg%n</pattern>
|
||||
</layout>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>WARN</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="fileError" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>./data/log/${log.base}/error/error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<FileNamePattern>./data/log/${log.base}/error/error.%d{yyyy-MM-dd}.log
|
||||
</FileNamePattern>
|
||||
<MaxHistory>30</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{256} - %msg%n</pattern>
|
||||
</layout>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>ERROR</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name ="asyncInfo" class= "ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold >100</discardingThreshold>
|
||||
<queueSize>1024</queueSize>
|
||||
<appender-ref ref ="fileInfo"/>
|
||||
</appender>
|
||||
|
||||
<appender name ="asyncWarn" class= "ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold >100</discardingThreshold>
|
||||
<queueSize>1024</queueSize>
|
||||
<appender-ref ref ="fileWarn"/>
|
||||
</appender>
|
||||
|
||||
<appender name ="asyncError" class= "ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold >100</discardingThreshold>
|
||||
<queueSize>1024</queueSize>
|
||||
<appender-ref ref ="fileError"/>
|
||||
</appender>
|
||||
|
||||
<!-- 控制台输出日志级别 -->
|
||||
<root level="info">
|
||||
<appender-ref ref="stdout" />
|
||||
<appender-ref ref="asyncInfo" />
|
||||
<appender-ref ref="asyncWarn" />
|
||||
<appender-ref ref="asyncError" />
|
||||
</root>
|
||||
</configuration>
|
@ -0,0 +1,2 @@
|
||||
#com.aizuda.easy.retry.client.core.serializer.HessianSerializer
|
||||
#com.aizuda.easy.retry.client.core.serializer.JacksonSerializer
|
@ -0,0 +1 @@
|
||||
#com.maluxinyu.easyretry.spi.TTLRetrySiteSnapshotContext
|
@ -0,0 +1 @@
|
||||
#com.maluxinyu.easyretry.spi.MyEasyRetryListener
|
Loading…
Reference in New Issue
Block a user