最佳实践:如何让自定义RestTemplate自动重试失败的API调用?
实现方案推荐
核心结论
绝对不要在ResponseErrorHandler的handleError()方法中处理重试逻辑:该方法被触发时,HTTP响应的输入流已经被标记为只读、仅可消费一次,在此处发起重试极易出现流已关闭、资源泄漏、请求上下文丢失的问题,该类的设计定位本身就是错误状态解析而非重试编排。
优先选择全局统一处理的方案,避免每个业务API调用处重复编写冗余代码,后期维护成本更低,仅在个别接口有特殊重试规则时再做单独适配。
推荐实现方案
方案1:Spring Retry 注解式实现(优先选择,侵入性最低)
适合需要灵活配置不同接口重试规则的场景,无需修改现有业务逻辑核心代码:
- 引入依赖(Spring Boot场景)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-retry</artifactId> </dependency>
- 启用重试功能:在启动类/配置类上添加
@EnableRetry注解 - 给需要重试的业务方法添加重试配置:
@Retryable( retryFor = [HttpClientErrorException.Unauthorized::class, ResourceAccessException::class], maxAttempts = 3, backoff = Backoff(delay = 1000, multiplier = 2) ) fun create(contentRequest: OrderSendXXXDto, accessToken: String): ResponseEntity<OrderXXXResponseDto> { // 原有业务逻辑不变 } // 可选:添加重试全部失败后的降级逻辑 @Recover fun createFallback(e: Exception, contentRequest: OrderSendXXXDto, accessToken: String): ResponseEntity<OrderXXXResponseDto> { // 自定义降级逻辑,比如抛出业务异常、返回默认值等 }
你可以直接在重试触发条件中配置仅当外部API抛出401异常时触发重试,和你现有的token刷新逻辑天然配合。
方案2:ClientHttpRequestInterceptor 全局拦截实现(全量请求统一规则)
适合所有走该RestTemplate的外部API请求都需要统一重试逻辑的场景,完全无需修改业务层代码:
- 先配置RestTemplate使用可缓存请求体的工厂,避免重试时请求体丢失:
@Bean fun restTemplate(): RestTemplate { val factory = BufferingClientHttpRequestFactory(SimpleClientHttpRequestFactory()) val restTemplate = RestTemplate(factory) // 注册自定义重试拦截器 restTemplate.interceptors.add(retryInterceptor()) // 注册你原有自定义的ExternalApiErrorHandler restTemplate.errorHandler = externalApiErrorHandler() return restTemplate }
- 编写自定义重试拦截器:
class RetryInterceptor( private val xxxAuthService: XXXAuthService ) : ClientHttpRequestInterceptor { override fun intercept( request: HttpRequest, body: ByteArray, execution: ClientHttpRequestExecution ): ClientHttpResponse { var retryCount = 0 val maxRetry = 3 while(retryCount < maxRetry) { val response = execution.execute(request, body) if (response.statusCode != HttpStatus.UNAUTHORIZED || !ExternalApiEnpoint.isExternalApiEndpoint(request.uri.toString())) { return response } // 401场景先刷新token再重试 xxxAuthService.callLogin() retryCount++ response.close() } throw RuntimeException("外部API请求重试${maxRetry}次后仍失败") } }
相关检索关键词
- Spring RestTemplate 全局重试实现
- Spring Retry 401 Token自动刷新
- RestTemplate ClientHttpRequestInterceptor 重试逻辑
- ResponseErrorHandler 重试 流关闭问题
- Spring RestTemplate 401 自动登录重试
内容的提问来源于stack exchange,提问作者Dattq2303
相关产品推荐
相关产品推荐

