一、spring retry
1. 基本原理
spring retry是spring官方提供的重试框架,作为spring生态系统的一部分,它通过aop(面向切面编程)实现了对方法调用的重试能力。当方法调用失败时,spring retry会根据配置的策略自动重新执行该方法,直到成功或达到最大重试次数。
spring retry的核心组件包括:
- retryoperations:定义重试操作的接口
- retrytemplate:retryoperations的默认实现
- retrypolicy:定义何时进行重试(如最大次数、重试的异常类型等)
- backoffpolicy:定义重试间隔策略(如固定间隔、指数退避等)
- recoverycallback:定义最终失败后的恢复策略
2. 集成配置
在springboot项目中集成spring retry:
<dependency>
<groupid>org.springframework.retry</groupid>
<artifactid>spring-retry</artifactid>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-aspects</artifactid>
</dependency>
然后在启动类上启用重试功能:
@springbootapplication
@enableretry
public class application {
public static void main(string[] args) {
springapplication.run(application.class, args);
}
}
3. 使用方法
spring retry提供了注解方式和编程方式两种使用方法。
注解方式
@service
public class remoteserviceclient {
private static final logger logger = loggerfactory.getlogger(remoteserviceclient.class);
@retryable(value = {serviceexception.class},
maxattempts = 3,
backoff = @backoff(delay = 1000, multiplier = 2))
public string callremoteservice(string param) {
logger.info("调用远程服务,参数: {}", param);
// 模拟调用失败
if (math.random() > 0.7) {
logger.error("服务调用失败");
throw new serviceexception("远程服务暂时不可用");
}
return "调用成功: " + param;
}
@recover
public string recover(serviceexception e, string param) {
logger.warn("重试失败,执行恢复方法, 参数: {}", param);
return "降级响应: " + param;
}
}
在上面的例子中:
@retryable注解定义了需要重试的方法,包括触发重试的异常类型、最大重试次数和退避策略backoff属性设置初始延迟1秒,且每次延迟时间翻倍(指数退避)@recover注解定义了重试失败后的恢复方法
编程方式
@service
public class remoteserviceclient {
private final retrytemplate retrytemplate;
@autowired
public remoteserviceclient(retrytemplate retrytemplate) {
this.retrytemplate = retrytemplate;
}
public string callwithretry(string param) {
return retrytemplate.execute(context -> {
// 重试的业务逻辑
if (math.random() > 0.7) {
throw new serviceexception("服务暂时不可用");
}
return "调用成功: " + param;
}, context -> {
// 重试失败后的恢复逻辑
return "降级响应: " + param;
});
}
@bean
public retrytemplate retrytemplate() {
retrytemplate template = new retrytemplate();
// 设置重试策略
simpleretrypolicy policy = new simpleretrypolicy();
policy.setmaxattempts(3);
// 设置退避策略
exponentialbackoffpolicy backoffpolicy = new exponentialbackoffpolicy();
backoffpolicy.setinitialinterval(1000);
backoffpolicy.setmultiplier(2.0);
template.setretrypolicy(policy);
template.setbackoffpolicy(backoffpolicy);
return template;
}
}
4. 优缺点
优点
- 与spring生态系统完美集成
- 提供了丰富的重试策略和配置选项
- 支持注解和编程两种方式,使用灵活
- 可以精确控制重试的异常类型
- 支持声明式事务回滚和提交
缺点
- 依赖spring框架
- 代码侵入性相对较强
- 在复杂场景下配置略显复杂
- 与其他容错机制集成需要额外工作
5. 适用场景
- spring生态系统的项目
- 需要精细控制重试条件和策略的场景
- 与spring事务结合的业务场景
- 方法级别的重试需求
二、resilience4j retry
1. 基本原理
resilience4j是受netflix hystrix启发而创建的轻量级容错库,其中resilience4j retry模块提供了强大的重试功能。与spring retry不同,resilience4j采用函数式编程风格,使用装饰器模式实现重试功能。
resilience4j retry的特点:
- 基于函数式接口
- 无外部依赖,轻量级设计
- 可与其他容错机制(如断路器、限流器)无缝集成
- 提供丰富的监控指标
2. 集成配置
在springboot项目中集成resilience4j retry:
<dependency>
<groupid>io.github.resilience4j</groupid>
<artifactid>resilience4j-spring-boot2</artifactid>
<version>1.7.0</version>
</dependency>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-aop</artifactid>
</dependency>
配置application.yml:
resilience4j.retry:
instances:
backendservice:
maxattempts: 3
waitduration: 1s
enableexponentialbackoff: true
exponentialbackoffmultiplier: 2
retryexceptions:
- java.io.ioexception
- java.util.concurrent.timeoutexception
3. 使用方法
resilience4j同样支持注解方式和编程方式。
注解方式
@service
public class backendservice {
private static final logger logger = loggerfactory.getlogger(backendservice.class);
@retry(name = "backendservice", fallbackmethod = "fallbackcall")
public string callbackendservice(string param) {
logger.info("调用后端服务,参数: {}", param);
if (math.random() > 0.7) {
logger.error("服务调用失败");
throw new ioexception("服务连接失败");
}
return "后端服务响应: " + param;
}
public string fallbackcall(string param, exception ex) {
logger.warn("所有重试失败,执行降级方法,参数: {}", param);
return "降级响应: " + param;
}
}
编程方式
@service
public class backendservice {
private final retryregistry retryregistry;
private final logger logger = loggerfactory.getlogger(backendservice.class);
@autowired
public backendservice(retryregistry retryregistry) {
this.retryregistry = retryregistry;
}
public string executewithretry(string param) {
// 获取已配置的重试实例
retry retry = retryregistry.retry("backendservice");
// 创建一个可重试的函数
checkedfunction0<string> retryablefunction = retry.decoratecheckedsupplier(
retry, () -> callbackendservice(param));
try {
// 执行重试函数
return retryablefunction.apply();
} catch (throwable throwable) {
logger.error("重试失败: {}", throwable.getmessage());
return "降级响应: " + param;
}
}
private string callbackendservice(string param) throws ioexception {
logger.info("调用后端服务,参数: {}", param);
if (math.random() > 0.7) {
throw new ioexception("服务连接失败");
}
return "后端服务响应: " + param;
}
}
4. 优缺点
优点
- 轻量级设计,无外部依赖
- 函数式编程风格,代码简洁
- 提供丰富的监控和统计指标
- 可与断路器、限流器等容错机制无缝集成
- 支持多种高级重试策略
缺点
- 学习曲线相对陡峭,尤其是函数式概念
- 对于不熟悉函数式编程的开发者可能不够直观
- 某些高级功能需要额外配置
5. 适用场景
- 需要与其他容错机制结合的复杂场景
- 微服务架构中的服务间调用
- 需要详细监控指标的系统
三、guava retrying
1. 基本原理
guava retrying是google guava库提供的重试机制,它提供了一个简单灵活的api来实现重试功能。
guava retrying通过构建器模式提供了灵活的重试配置,可以自定义重试条件、停止策略、等待策略等。
2. 集成配置
在springboot项目中集成guava retrying:
<dependency>
<groupid>com.github.rholder</groupid>
<artifactid>guava-retrying</artifactid>
<version>2.0.0</version>
</dependency>
3. 使用方法
guava retrying主要采用编程方式使用:
@service
public class externalserviceclient {
private static final logger logger = loggerfactory.getlogger(externalserviceclient.class);
public string callexternalservice(string param) {
retryer<string> retryer = retryerbuilder.<string>newbuilder()
.retryifexception() // 发生任何异常时重试
.retryifresult(result -> result == null) // 结果为null时重试
.withwaitstrategy(waitstrategies.exponentialwait(1000, 10000, timeunit.milliseconds)) // 指数退避
.withstopstrategy(stopstrategies.stopafterattempt(3)) // 最多重试3次
.withretrylistener(new retrylistener() {
@override
public <v> void onretry(attempt<v> attempt) {
logger.info("第{}次重试", attempt.getattemptnumber());
if (attempt.hasexception()) {
logger.error("异常: {}", attempt.getexceptioncause().getmessage());
}
}
})
.build();
try {
return retryer.call(() -> {
logger.info("调用外部服务,参数: {}", param);
// 模拟服务调用
if (math.random() > 0.7) {
throw new runtimeexception("服务暂时不可用");
}
return "外部服务响应: " + param;
});
} catch (retryexception | executionexception e) {
logger.error("重试失败: {}", e.getmessage());
return "降级响应: " + param;
}
}
}
在springboot中创建可复用的retryer bean:
@configuration
public class retryconfig {
@bean
public <t> retryer<t> defaultretryer() {
return retryerbuilder.<t>newbuilder()
.retryifexception()
.retryifresult(predicates.isnull())
.withwaitstrategy(waitstrategies.exponentialwait(100, 1000, timeunit.milliseconds))
.withstopstrategy(stopstrategies.stopafterattempt(3))
.build();
}
}
@service
public class servicewithretry {
private final retryer<string> retryer;
@autowired
public servicewithretry(retryer<string> retryer) {
this.retryer = retryer;
}
public string executewithretry(string input) throws executionexception, retryexception {
return retryer.call(() -> {
// 业务逻辑
return processinput(input);
});
}
}
4. 高级特性
guava retrying提供了丰富的定制选项:
retryer<string> complexretryer = retryerbuilder.<string>newbuilder()
// 定制重试条件
.retryifexceptionoftype(ioexception.class)
.retryifexception(e -> e instanceof timeoutexception)
.retryifresult(result -> result != null && result.contains("error"))
// 定制等待策略
.withwaitstrategy(waitstrategies.join(
waitstrategies.fixedwait(1000, timeunit.milliseconds),
waitstrategies.randomwait(1000, timeunit.milliseconds, 2000, timeunit.milliseconds)
))
// 定制停止策略
.withstopstrategy(stopstrategies.stopafterdelay(30, timeunit.seconds))
// 定制阻塞策略
.withblockstrategy(blockstrategies.threadsleepstrategy())
.build();
5. 优缺点
优点
- api简单直观,容易上手
- 高度可定制的重试条件、等待策略和停止策略
- 不依赖spring框架,可在任何java项目中使用
缺点
- 没有注解支持,只能通过编程方式使用
- 缺乏与spring生态系统的深度集成
- 没有内置的监控和统计功能
- 已停止更新
6. 适用场景
- 简单的重试需求
- 非spring项目或对spring依赖较少的项目
- 需要高度自定义重试逻辑的场景
四、failsafe
1. 基本原理
failsafe是一个相对较新的java重试库,专注于高性能和低延迟场景。它的设计目标是提供一个简单、高效的重试机制,同时保持api的简洁性和使用的便捷性。failsafe支持同步和异步重试,具有灵活的重试策略和最小的依赖。
2. 集成配置
在springboot项目中集成failsafe
<dependency>
<groupid>dev.failsafe</groupid>
<artifactid>failsafe</artifactid>
<version>3.3.2</version>
</dependency>
failsafe通常通过dev.failsafe:failsafe库来使用,这是一个现代化的重试和容错库。
3. 使用方法
failsafe主要采用编程方式使用,具有流式api设计
@service
public class failsafeservice {
private static final logger logger = loggerfactory.getlogger(failsafeservice.class);
public string executewithretry(string param) {
return failsafe.with(
// 配置重试策略
retrypolicy.<string>builder()
.handle(ioexception.class, timeoutexception.class)
.withmaxretries(3)
.withdelay(duration.ofseconds(1))
.withmaxduration(duration.ofseconds(10))
.withbackoff(duration.ofmillis(100), duration.ofseconds(2))
.onretry(event -> logger.info("第{}次重试,上次异常: {}",
event.getattemptcount(),
event.getlastexception().getmessage()))
.onfailure(event -> logger.error("重试失败,尝试次数: {}, 总耗时: {}ms",
event.getattemptcount(),
event.getelapsedtime().tomillis()))
.build()
)
.get(() -> {
logger.info("执行操作,参数: {}", param);
// 模拟操作
if (math.random() > 0.7) {
throw new ioexception("操作暂时失败");
}
return "操作成功: " + param;
});
}
// 异步重试示例
public completablefuture<string> executewithretryasync(string param) {
return failsafe.with(
retrypolicy.<string>builder()
.handle(ioexception.class)
.withmaxretries(3)
.withbackoff(duration.ofmillis(100), duration.ofseconds(1))
.build()
)
.getasync(() -> {
logger.info("异步执行操作,参数: {}", param);
// 模拟异步操作
if (math.random() > 0.7) {
throw new ioexception("异步操作暂时失败");
}
return "异步操作成功: " + param;
});
}
// 带降级的重试示例
public string executewithfallback(string param) {
return failsafe.with(
retrypolicy.<string>builder()
.handle(ioexception.class)
.withmaxretries(3)
.build(),
// 降级策略
fallback.of(e -> "降级响应: " + param)
)
.get(() -> {
// 业务逻辑
if (math.random() > 0.7) {
throw new ioexception("操作失败");
}
return "操作成功: " + param;
});
}
}
在springboot中创建可复用的retrypolicy bean
@configuration
public class failsafeconfig {
@bean
public retrypolicy<object> defaultretrypolicy() {
return retrypolicy.builder()
.handle(exception.class)
.withmaxretries(3)
.withbackoff(duration.ofmillis(100), duration.ofseconds(1), 2.0)
.build();
}
@bean
public fallback<object> defaultfallback() {
return fallback.of(e -> {
if (e instanceof serviceexception) {
return "服务异常降级";
}
return "通用降级响应";
});
}
}
@service
public class servicewithfailsaferetry {
private final retrypolicy<object> defaultretrypolicy;
private final fallback<object> defaultfallback;
@autowired
public servicewithfailsaferetry(retrypolicy<object> defaultretrypolicy,
fallback<object> defaultfallback) {
this.defaultretrypolicy = defaultretrypolicy;
this.defaultfallback = defaultfallback;
}
public string executewithretry(string input) {
return failsafe.with(defaultretrypolicy, defaultfallback)
.get(() -> {
// 业务逻辑
return processinput(input);
});
}
}
4. 优缺点
优点
- 极高的性能,适合高频调用场景
- 支持同步和异步重试
- 轻量级,依赖少
- 与completablefuture良好集成
- 内置丰富的监听器机制
缺点
- 没有注解支持,只能通过编程方式使用
- 与spring框架集成度不高
- 近几年更新也不活跃
5. 适用场景
- 高性能、低延迟要求的应用
- 需要异步重试能力的场景
- 需要细粒度控制重试行为的场景
五、四种重试机制的对比
| 特性 | spring retry | resilience4j retry | guava retrying | failsafe |
|---|---|---|---|---|
| 编程模型 | aop + 命令式 | 函数式 | 命令式 | 流式 |
| 注解支持 | 支持 | 支持 | 不支持 | 不支持 |
| 依赖 | spring | 无外部依赖 | guava | 最小依赖 |
| 性能开销 | 中等 | 低 | 中等 | 极低 |
| 异步支持 | 有限 | 良好 | 有限 | 优秀 |
| 监控集成 | 有限 | 丰富 | 无 | 基本 |
| 配置方式 | 注解/编程 | 配置文件/注解/编程 | 编程 | 编程 |
| 与其他容错机制集成 | 有限 | 原生支持 | 无 | 良好 |
| 学习曲线 | 中等 | 较陡 | 平缓 | 平缓 |
| 可定制性 | 高 | 高 | 高 | 高 |
| 适用场景 | spring项目 | 微服务/云原生应用 | 简单场景/非spring项目 | 高性能场景 |
六、最佳实践与注意事项
1. 通用最佳实践
- 确保幂等性:重试机制最适合用于幂等操作,即多次执行产生相同结果的操作。对于非幂等操作,需要特别小心。
- 设置合理的超时和重试次数:避免无限重试或重试时间过长,通常3-5次足够处理大多数临时故障。
- 使用指数退避策略:随着重试次数增加,逐渐增加重试间隔,避免对目标服务造成过大压力。
- 区分临时故障和永久故障:只对可能自行恢复的临时故障进行重试,对于永久性错误不应重试。
- 添加监控和日志:记录重试次数、成功率等指标,便于问题排查和性能优化。
2. 避免常见陷阱
- 重试风暴:当多个客户端同时对一个故障服务进行重试时,可能导致服务负载激增。
- 资源泄漏:重试过程中要确保资源(如数据库连接、http连接)正确释放。
- 过度重试:过度重试可能导致性能下降,应设置合理的最大重试次数和总超时时间。
- 重试成本:某些操作重试成本高昂(如涉及第三方付费api),需谨慎设计重试策略。
七、总结
选择合适的重试机制应基于项目的技术栈、复杂度和需求。无论选择哪种机制,都应遵循重试的最佳实践,避免常见陷阱,确保系统的稳定性和可靠性。
以上就是springboot实现重试机制的四种方案的详细内容,更多关于springboot重试机制的资料请关注代码网其它相关文章!
发表评论