Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
basic-api-boot
概览
概览
详情
活动
周期分析
版本库
存储库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
问题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
Basic
basic-api-boot
Commits
729ea163
提交
729ea163
authored
5月 10, 2024
作者:
方治民
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 新增 RetryExecutor 重试执行器,可用于处理可能出现异常但被允许重试的业务场景,封装简化 AOP 的切面重试实现,借助 Spring Retry 实现
上级
b5f73560
隐藏空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
202 行增加
和
2 行删除
+202
-2
ExampleController.java
...in/java/com/yiring/app/web/example/ExampleController.java
+11
-1
RetryTests.java
app/src/test/java/com/yiring/RetryTests.java
+73
-0
build.gradle
basic-common/util/build.gradle
+3
-0
RetryExecutor.java
...l/src/main/java/com/yiring/common/core/RetryExecutor.java
+114
-0
build.gradle
build.gradle
+1
-1
没有找到文件。
app/src/main/java/com/yiring/app/web/example/ExampleController.java
浏览文件 @
729ea163
...
...
@@ -14,6 +14,7 @@ import com.yiring.common.annotation.DownloadResponse;
import
com.yiring.common.annotation.RateLimiter
;
import
com.yiring.common.core.I18n
;
import
com.yiring.common.core.Result
;
import
com.yiring.common.core.RetryExecutor
;
import
com.yiring.common.core.Status
;
import
com.yiring.common.param.IdParam
;
import
com.yiring.common.param.PageParam
;
...
...
@@ -60,6 +61,7 @@ public class ExampleController {
final
Auths
auths
;
final
UserExtensionRepository
userExtensionRepository
;
final
FileManageService
fileManageService
;
final
RetryExecutor
retryExecutor
;
@RateLimiter
(
count
=
1
)
@Operation
(
summary
=
"Hello World"
)
...
...
@@ -75,11 +77,19 @@ public class ExampleController {
@GetMapping
(
"fail"
)
public
void
fail
()
{
// 1. 直接抛出异常
Status
.
BAD_REQUEST
.
expose
(
"Code.1"
);
//
Status.BAD_REQUEST.expose("Code.1");
// 2. 手动抛出异常
// throw Status.BAD_REQUEST.exception("Code.1");
// throw BusinessException.i18n("Code.1");
// throw new FailStatusException(Status.BAD_REQUEST, "Code.1");
// 3. 重试抛出异常
throw
retryExecutor
.
run
(
ctx
->
{
throw
new
RuntimeException
(
"retry test fail"
);
},
ctx
->
Status
.
BAD_REQUEST
.
exception
(
"Code.1"
),
template
->
RetryExecutor
.
setSimplePolicy
(
template
,
3
,
1000L
)
);
}
@SaCheckLogin
...
...
app/src/test/java/com/yiring/RetryTests.java
0 → 100644
浏览文件 @
729ea163
/* (C) 2024 YiRing, Inc. */
package
com
.
yiring
;
import
cn.hutool.core.lang.Console
;
import
com.yiring.common.core.RetryExecutor
;
import
org.junit.jupiter.api.Test
;
import
org.springframework.boot.SpringBootConfiguration
;
import
org.springframework.boot.test.context.SpringBootTest
;
import
org.springframework.retry.annotation.EnableRetry
;
@SpringBootConfiguration
@SpringBootTest
@EnableRetry
public
class
RetryTests
{
RetryExecutor
retry
=
new
RetryExecutor
();
/**
* 简易重试,超过默认次数会抛出异常
*/
@Test
void
def
()
{
Integer
value
=
retry
.
run
(
ctx
->
{
Console
.
log
(
"retry test, {}"
,
ctx
.
getRetryCount
());
String
text
=
"0.0"
;
if
(
ctx
.
getRetryCount
()
>
1
)
{
text
=
"1"
;
}
return
Integer
.
parseInt
(
text
);
});
Console
.
log
(
"[default] retry test result: {}"
,
value
);
}
/**
* 默认的退回策略
*/
@Test
void
recover
()
{
Integer
value
=
retry
.
run
(
ctx
->
{
Console
.
log
(
"retry test, {}"
,
ctx
.
getRetryCount
());
throw
new
RuntimeException
(
"test"
);
},
ctx
->
{
Console
.
log
(
"retry recover, {}"
,
ctx
.
getRetryCount
());
return
666
;
},
template
->
RetryExecutor
.
setSimplePolicy
(
template
,
2
,
1000L
)
);
Console
.
log
(
"[recover] retry test result: {}"
,
value
);
}
/**
* 重试 2 次,每次间隔 5s,失败默认退回 null
*/
@Test
void
custom
()
{
Integer
value
=
retry
.
run
(
ctx
->
{
Console
.
log
(
"retry test, {}"
,
ctx
.
getRetryCount
());
throw
new
RuntimeException
(
"test"
);
},
ctx
->
{
Console
.
log
(
"retry recover, {}"
,
ctx
.
getRetryCount
());
return
null
;
},
template
->
RetryExecutor
.
setSimplePolicy
(
template
,
2
,
5000L
)
);
Console
.
log
(
"[recover] retry test result: {}"
,
value
);
}
}
basic-common/util/build.gradle
浏览文件 @
729ea163
...
...
@@ -3,6 +3,9 @@ dependencies {
implementation
'org.springframework.boot:spring-boot-starter-web'
implementation
'org.springframework.boot:spring-boot-starter-validation'
// Spring Retry
api
"org.springframework.retry:spring-retry"
// hutool
implementation
"cn.hutool:hutool-core:${hutoolVersion}"
implementation
"cn.hutool:hutool-http:${hutoolVersion}"
...
...
basic-common/util/src/main/java/com/yiring/common/core/RetryExecutor.java
0 → 100644
浏览文件 @
729ea163
/* (C) 2024 YiRing, Inc. */
package
com
.
yiring
.
common
.
core
;
import
jakarta.validation.constraints.NotNull
;
import
java.util.Objects
;
import
java.util.function.Consumer
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.retry.RetryContext
;
import
org.springframework.retry.annotation.EnableRetry
;
import
org.springframework.retry.backoff.FixedBackOffPolicy
;
import
org.springframework.retry.policy.SimpleRetryPolicy
;
import
org.springframework.retry.support.RetryTemplate
;
import
org.springframework.stereotype.Component
;
/**
* 重试执行器
* 用途: 主要用于解决切面重试机制的问题,减少自定义实现 AOP 重试机制的代码量
*
* @author Jim
*/
@SuppressWarnings
(
"unused"
)
@Slf4j
@Component
@EnableRetry
public
class
RetryExecutor
{
/**
* 包装重试方法实现
* 默认 RetryTemplate 配置为 1s 重试间隔 重试 3 次,可用过自定义参数配置
*
* @param accomplish 重试业务实现
* @param <S> 重试业务的返回值类型
* @return 重试业务的返回值,例如:一个连接对象、网络请求结果
*/
public
<
S
>
S
run
(
@NotNull
RetrySupplier
<
S
>
accomplish
)
{
return
run
(
accomplish
,
null
,
null
);
}
/**
* 包装重试方法实现
* 默认 RetryTemplate 配置为 1s 重试间隔 重试 3 次,可用过自定义参数配置
*
* @param accomplish 重试业务实现
* @param consumer 重试模板
* @param <S> 重试业务的返回值类型
* @return 重试业务的返回值,例如:一个连接对象、网络请求结果
*/
public
<
S
>
S
run
(
@NotNull
RetrySupplier
<
S
>
accomplish
,
Consumer
<
RetryTemplate
>
consumer
)
{
return
run
(
accomplish
,
null
,
consumer
);
}
/**
* 包装重试方法实现
*
* @param accomplish 重试业务实现
* @param recover 重试失败的回退实现
* @param consumer 重试模板
* @param <S> 重试业务的返回值类型
* @return 重试业务的返回值,例如:一个连接对象、网络请求结果
*/
public
<
S
>
S
run
(
@NotNull
RetrySupplier
<
S
>
accomplish
,
RetrySupplier
<
S
>
recover
,
Consumer
<
RetryTemplate
>
consumer
)
{
// 创建默认的重试模板
RetryTemplate
template
=
RetryTemplate
.
builder
().
retryOn
(
Exception
.
class
).
build
();
// 重试模板的自定义实现,例如一些重试机制的配置
if
(
Objects
.
nonNull
(
consumer
))
{
consumer
.
accept
(
template
);
}
// 通过模板执行重试实现机制和默认的回退实现
return
template
.
execute
(
accomplish:
:
get
,
ctx
->
{
if
(
Objects
.
nonNull
(
recover
))
{
return
recover
.
get
(
ctx
);
}
// 没有添加保底恢复实现的情况下,抛出原始异常
Throwable
throwable
=
ctx
.
getLastThrowable
();
log
.
error
(
"[RetryExecutor] execute fail, retry count: {}, e: {}"
,
ctx
.
getRetryCount
(),
throwable
.
getMessage
()
);
throw
new
RuntimeException
(
throwable
);
}
);
}
/**
* 设置简单的重试策略
*
* @param template 重试模板
* @param maxAttempts 最大重试次数
* @param backOffPeriod 重试间隔时间
*/
public
static
void
setSimplePolicy
(
RetryTemplate
template
,
int
maxAttempts
,
long
backOffPeriod
)
{
// 设置简单的重试次数
SimpleRetryPolicy
simpleRetryPolicy
=
new
SimpleRetryPolicy
();
simpleRetryPolicy
.
setMaxAttempts
(
maxAttempts
);
template
.
setRetryPolicy
(
simpleRetryPolicy
);
// 设置固定的重试时间间隔,单位: ms 毫秒
FixedBackOffPolicy
backOffPolicy
=
new
FixedBackOffPolicy
();
backOffPolicy
.
setBackOffPeriod
(
backOffPeriod
);
template
.
setBackOffPolicy
(
backOffPolicy
);
}
@FunctionalInterface
public
interface
RetrySupplier
<
R
>
{
R
get
(
RetryContext
ctx
);
}
}
build.gradle
浏览文件 @
729ea163
...
...
@@ -129,7 +129,7 @@ subprojects {
gradle
.
taskGraph
.
whenReady
{
tasks
.
each
{
task
->
if
(
task
.
name
.
contains
(
"test"
))
{
task
.
enabled
=
fals
e
task
.
enabled
=
tru
e
}
}
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论