交通信用网站建设,网站地图 用户体验,wordpress stats,可以申请做cpa广告的网站目录
一、核心设计理念
二、快速入门#xff1a;核心依赖与启用
1. 引入依赖
2. 启用 Spring Retry
三、核心使用方式
方式 1#xff1a;声明式重试#xff08;注解方式#xff0c;推荐#xff09;
1. 基础示例#xff08;结合 OpenFeign#xff09;
2. 注解参数…目录一、核心设计理念二、快速入门核心依赖与启用1. 引入依赖2. 启用 Spring Retry三、核心使用方式方式 1声明式重试注解方式推荐1. 基础示例结合 OpenFeign2. 注解参数详解方式 2编程式重试手动控制1. 配置 RetryTemplate Bean2. 编程式调用示例方式 3全局默认配置四、高级特性1. 重试监听监控重试过程2. 条件重试按返回值重试3. 与熔断组件集成Sentinel/Hystrix五、生产环境最佳实践1. 核心配置建议2. 关键注意事项1严格区分幂等请求2重试与超时的配合3避免重试叠加4日志与监控5降级逻辑必须有六、常见问题排查1. 重试不生效2. 降级方法不执行3. 重试导致重复数据4. 重试次数超出预期七、总结Spring Retry 是 Spring 生态中用于声明式 / 编程式重试的框架基于 AOP 实现支持灵活的重试策略、退避策略、降级处理是生产环境中实现可靠重试的首选方案。本文从核心概念、使用方式、高级特性、与 OpenFeign 集成等维度全面讲解 Spring Retry 的设计与实践。一、核心设计理念Spring Retry 的核心目标是对不稳定的操作如网络调用、数据库访问进行容错处理通过重试降低临时故障的影响同时提供降级机制保证系统可用性。其核心设计包含 3 个核心组件组件作用重试策略RetryPolicy决定「何时重试」如异常类型、重试次数、返回值条件退避策略BackOffPolicy决定「重试间隔」如固定间隔、指数退避、随机间隔重试模板RetryTemplate重试的核心执行器整合重试策略和退避策略提供统一的重试入口二、快速入门核心依赖与启用1. 引入依赖Spring Retry 依赖 AOP 实现需同时引入spring-retry和spring-aspectsxml!-- Maven 依赖 -- dependencies !-- Spring Retry 核心 -- dependency groupIdorg.springframework.retry/groupId artifactIdspring-retry/artifactId version1.3.4/version !-- 适配 Spring Boot 2.x3.x 可使用 2.x 版本 -- /dependency !-- AOP 依赖必须 -- dependency groupIdorg.springframework/groupId artifactIdspring-aspects/artifactId version5.3.30/version /dependency /dependencies2. 启用 Spring Retry在 Spring Boot 启动类添加EnableRetry注解开启重试功能java运行import org.springframework.retry.annotation.EnableRetry; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication EnableRetry // 启用重试核心注解 public class RetryDemoApplication { public static void main(String[] args) { SpringApplication.run(RetryDemoApplication.class, args); } }三、核心使用方式方式 1声明式重试注解方式推荐通过Retryable、Recover注解快速实现重试无需侵入业务代码是最常用的方式。1. 基础示例结合 OpenFeignjava运行import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.retry.annotation.Recover; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; FeignClient(name product-service, url ${product.service.url}) public interface ProductFeignClient { /** * 声明式重试配置 * - 仅对指定异常重试网络超时、5xx 错误 * - 最大重试 2 次首次请求 2 次重试 共 3 次 * - 重试间隔初始 1 秒指数退避第 2 次重试间隔 2 秒 */ Retryable( // 触发重试的异常类型数组 include { java.net.SocketTimeoutException.class, // 网络超时 feign.RetryableException.class, // Feign 重试异常 org.springframework.web.client.HttpServerErrorException.class // 5xx 错误 }, // 排除不重试的异常 exclude { org.springframework.web.client.HttpClientErrorException.class // 4xx 客户端错误 }, // 最大重试次数不含首次请求默认 3 maxAttempts 2, // 退避策略initialInterval 初始间隔毫秒multiplier 指数倍数 backoff Backoff(initialInterval 1000, multiplier 2, maxInterval 3000) ) GetMapping(/product/{id}) ProductDTO getProductById(PathVariable(id) Long id); /** * 重试耗尽后的降级方法核心避免重试失败后直接抛异常 * 规则 * 1. 方法名任意需添加 Recover 注解 * 2. 参数必须包含重试方法的所有参数 触发重试的异常类型顺序可调整 * 3. 返回值必须与重试方法一致 * 4. 若为 Feign 接口需定义为 default 方法Java 8 支持。 */ Recover default ProductDTO recoverProduct(Exception e, Long id) { // 降级逻辑返回默认值、空值或记录告警日志 System.err.printf(获取商品失败id%d异常%s%n, id, e.getMessage()); return new ProductDTO().setId(id).setName(默认商品).setPrice(0.0); } }2. 注解参数详解注解参数类型默认值作用RetryableincludeClass?[]{}指定触发重试的异常类型优先级高于 excludeexcludeClass?[]{}指定不触发重试的异常类型maxAttemptsint3最大重试次数不含首次请求backoffBackoffBackoff()退避策略配置valueClass?[]{}等同于 include为了简化书写value {Exception.class}labelString重试标签用于监控 / 日志BackoffinitialIntervallong1000初始重试间隔毫秒multiplierdouble1.0指数退避倍数1.0 固定间隔2.0 每次间隔翻倍maxIntervallong0最大重试间隔毫秒0 无限制delaylong0固定重试间隔优先级高于 initialInterval不推荐混用randombooleanfalse是否随机调整重试间隔避免服务峰值Recover无--标记重试耗尽后的降级方法方式 2编程式重试手动控制通过RetryTemplate手动构建重试逻辑灵活性更高适合需要动态调整重试策略的场景。1. 配置 RetryTemplate Beanjava运行import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.policy.CompositeRetryPolicy; import org.springframework.retry.policy.SimpleRetryPolicy; import org.springframework.retry.policy.TimeoutRetryPolicy; import org.springframework.retry.support.RetryTemplate; Configuration public class RetryTemplateConfig { /** * 自定义重试模板支持组合策略次数限制 超时限制 */ Bean public RetryTemplate feignRetryTemplate() { RetryTemplate retryTemplate new RetryTemplate(); // 1. 重试策略组合策略次数 超时 CompositeRetryPolicy compositeRetryPolicy new CompositeRetryPolicy(); // 1.1 次数策略最大重试 2 次仅对指定异常重试 SimpleRetryPolicy simpleRetryPolicy new SimpleRetryPolicy(); simpleRetryPolicy.setMaxAttempts(2); // 重试次数不含首次 // 指定可重试的异常key异常类型valuetrue可重试 simpleRetryPolicy.setRetryableExceptions( java.net.SocketTimeoutException.class, true, feign.RetryableException.class, true ); // 指定不可重试的异常 simpleRetryPolicy.setFatalExceptions( org.springframework.web.client.HttpClientErrorException.class ); // 1.2 超时策略重试总耗时不超过 5 秒 TimeoutRetryPolicy timeoutRetryPolicy new TimeoutRetryPolicy(); timeoutRetryPolicy.setTimeout(5000); // 毫秒 compositeRetryPolicy.setPolicies(new SimpleRetryPolicy[]{simpleRetryPolicy, timeoutRetryPolicy}); retryTemplate.setRetryPolicy(compositeRetryPolicy); // 2. 退避策略指数退避 ExponentialBackOffPolicy backOffPolicy new ExponentialBackOffPolicy(); backOffPolicy.setInitialInterval(1000); // 初始间隔 1 秒 backOffPolicy.setMaxInterval(3000); // 最大间隔 3 秒 backOffPolicy.setMultiplier(2); // 间隔翻倍1s → 2s → 3s retryTemplate.setBackOffPolicy(backOffPolicy); return retryTemplate; } }2. 编程式调用示例java运行import org.springframework.retry.support.RetryTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; Service public class ProductService { Resource private ProductFeignClient productFeignClient; Resource private RetryTemplate feignRetryTemplate; /** * 编程式重试调用 Feign 接口 */ public ProductDTO getProduct(Long id) { try { // execute 方法 // - 参数1RetryCallback重试逻辑 // - 参数2RecoveryCallback降级逻辑可选 return feignRetryTemplate.execute( // 重试逻辑调用 Feign 接口 context - { System.out.printf(第 %d 次调用商品接口id%d%n, context.getRetryCount() 1, id); return productFeignClient.getProductById(id); }, // 降级逻辑重试耗尽后执行 context - { System.err.printf(重试耗尽id%d失败次数%d%n, id, context.getRetryCount()); return new ProductDTO().setId(id).setName(降级商品); } ); } catch (Exception e) { // 兜底异常处理 System.err.println(获取商品异常 e.getMessage()); return new ProductDTO(); } } }方式 3全局默认配置通过Retryable的默认属性或自定义RetryTemplate实现全局重试规则避免重复配置。java运行import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.retry.annotation.Retryable; import org.springframework.retry.annotation.RetryConfiguration; // 全局重试配置覆盖默认的 RetryConfiguration Configuration public class GlobalRetryConfig extends RetryConfiguration { /** * 全局默认 RetryTemplate */ Override Bean public RetryTemplate retryTemplate() { RetryTemplate retryTemplate new RetryTemplate(); // 配置全局重试策略参考方式2 return retryTemplate; } /** * 全局 Retryable 注解的默认属性可选 */ Bean public Retryable defaultRetryable() { // 通过注解属性设置全局默认值 return new Retryable() { Override public Class? extends Annotation annotationType() { return Retryable.class; } Override public Class?[] include() { return new Class[]{feign.RetryableException.class}; } Override public int maxAttempts() { return 2; } // 其他属性默认值... }; } }四、高级特性1. 重试监听监控重试过程通过RetryListener监听重试的开始、异常、完成事件用于日志记录、监控告警java运行import org.springframework.retry.RetryCallback; import org.springframework.retry.RetryContext; import org.springframework.retry.RetryListener; import org.springframework.stereotype.Component; Component public class CustomRetryListener implements RetryListener { /** * 重试开始前触发 */ Override public T, E extends Throwable boolean open(RetryContext context, RetryCallbackT, E callback) { System.out.println(重试开始上下文 context); return true; // 返回 true 继续重试false 终止 } /** * 每次重试异常时触发 */ Override public T, E extends Throwable void onError(RetryContext context, RetryCallbackT, E callback, Throwable throwable) { System.err.printf(重试失败次数%d异常%s%n, context.getRetryCount(), throwable.getMessage()); // 可在这里接入告警如钉钉、短信 } /** * 重试结束后触发成功/失败 */ Override public T, E extends Throwable void close(RetryContext context, RetryCallbackT, E callback, Throwable throwable) { if (throwable null) { System.out.println(重试成功总次数 context.getRetryCount()); } else { System.err.println(重试耗尽总次数 context.getRetryCount()); } } }2. 条件重试按返回值重试默认重试仅基于异常若需按返回值重试如返回 null、空列表需自定义RetryPolicyjava运行import org.springframework.retry.RetryContext; import org.springframework.retry.policy.AbstractRetryPolicy; import org.springframework.stereotype.Component; Component public class ReturnValueRetryPolicy extends AbstractRetryPolicy { Override public boolean canRetry(RetryContext context) { // 获取方法返回值 Object result context.getAttribute(result); // 条件返回 null 则重试 return result null context.getRetryCount() 2; } Override public void registerThrowable(RetryContext context, Throwable throwable) { super.registerThrowable(context, throwable); } Override public RetryContext open(RetryContext parent) { return new DefaultRetryContext(parent, this); } }3. 与熔断组件集成Sentinel/Hystrix生产环境中重试需与熔断配合避免重试导致服务雪崩java运行import com.alibaba.csp.sentinel.annotation.SentinelResource; import org.springframework.retry.annotation.Retryable; FeignClient(name order-service) public interface OrderFeignClient { Retryable(maxAttempts 1, backoff Backoff(initialInterval 1000)) SentinelResource( value getOrder, fallback fallbackGetOrder, // 熔断降级 blockHandler blockGetOrder // 流控降级 ) GetMapping(/order/{id}) OrderDTO getOrderById(PathVariable(id) Long id); // 熔断降级方法 default OrderDTO fallbackGetOrder(Long id) { return new OrderDTO(); } // 流控降级方法 default OrderDTO blockGetOrder(Long id, com.alibaba.csp.sentinel.slots.block.BlockException e) { return new OrderDTO(); } }五、生产环境最佳实践1. 核心配置建议场景重试次数重试间隔触发条件降级策略核心服务如订单1 次1 秒固定5xx / 超时返回默认值 告警非核心服务如商品2 次1s → 2s指数5xx / 超时 / 网络异常返回空值 日志外部接口如支付0-1 次2 秒固定仅 503 / 超时人工重试 降级提示2. 关键注意事项1严格区分幂等请求✅ 允许重试GET查询、PUT更新、DELETE幂等删除❌ 禁止重试POST创建、PATCH部分更新除非做了幂等性设计如请求 ID 去重。2重试与超时的配合Feign 超时时间需包含单次请求超时 重试总耗时yaml# application.yml feign: client: config: default: connectTimeout: 1000 # 连接超时 1 秒 readTimeout: 3000 # 单次读取超时 3 秒 # 重试 2 次 → 总超时 3*(21) 9 秒需确保业务超时 9 秒3避免重试叠加禁止同时使用「Feign 原生 Retryer」和「Spring Retry」否则会导致重复重试若使用 Spring Retry需禁用 Feign 原生重试java运行Bean public Retryer feignRetryer() { return Retryer.NEVER_RETRY; // 禁用 Feign 原生重试 }4日志与监控记录每次重试的原因、次数、耗时便于排查问题对重试次数超过阈值的接口告警如 1 分钟内重试 10 次监控重试成功率作为服务稳定性的指标。5降级逻辑必须有重试耗尽后必须通过Recover或RecoveryCallback提供降级逻辑降级逻辑需轻量无远程调用避免降级方法又触发异常。六、常见问题排查1. 重试不生效检查是否引入spring-aspects依赖AOP 核心检查启动类是否添加EnableRetry检查Retryable的include是否包含目标异常类型Feign 接口的Recover方法需定义为default方法否则无法识别。2. 降级方法不执行降级方法参数必须包含「重试方法参数 异常类型」返回值必须与重试方法一致若重试次数未耗尽如 maxAttempts2仅失败 1 次降级方法不会执行。3. 重试导致重复数据立即停止重试检查请求类型是否为 POST对写请求添加幂等性控制如基于业务唯一键、请求 ID 去重。4. 重试次数超出预期区分maxAttempts的定义Spring Retry 是「不含首次」Feign 原生是「含首次」检查是否配置了多个重试策略如全局 局部。七、总结Spring Retry 是 OpenFeign 实现可靠重试的最佳选择其核心价值在于声明式注解Retryable简化重试配置无侵入灵活的策略重试次数、退避间隔、异常类型适配不同场景完善的降级机制Recover保证服务可用性可与熔断、监控集成适配生产环境。使用时需牢记重试是兜底手段核心还是要提升服务本身的稳定性重试必须配合幂等性和降级否则会引发新的问题。