redis连接失败时默认不重试,需用@retryable注解或retrytemplate显式配置;拦截器无效因无法覆盖非web场景,且易破坏幂等性;lettuce自动重连不替代业务层重试,二者需协同并注意超时匹配。

Redis连接失败时,RedisTemplate 默认不重试,直接抛 RedisConnectionFailureException。想让服务在临时网络抖动或Redis短暂不可用时自动恢复,必须显式配置重试逻辑——但不能靠拦截器“全局兜底”,也不能把 RetryTemplate 硬塞进每个 opsForValue().get() 调用里。
为什么不能用拦截器统一捕获Redis异常做重试
拦截器作用于HTTP请求生命周期,而Redis操作可能发生在任意线程、定时任务、消息监听器甚至异步回调中。用 HandlerInterceptor 拦截不到这些场景的 RedisConnectionFailureException;强行在 preHandle 里初始化连接也无意义——连接失败通常发生在执行阶段,不是请求进入时。
- 拦截器只对
DispatcherServlet流转的 Web 请求有效,@Scheduled或@RabbitListener中的 Redis 调用完全绕过它 - 即使在 Web 层捕获到异常,重试逻辑若写在拦截器里,会污染横切关注点:你得手动提取原始方法参数、重建调用上下文,极易出错
- 更严重的是,重复执行写操作(如
setIfAbsent)可能破坏幂等性,而拦截器无法区分读/写语义
正确做法:用 @Retryable 注解 + RetryTemplate 组合控制粒度
Spring Retry 提供两种主流方式:声明式(@Retryable)和编程式(RetryTemplate)。前者适合业务方法级重试,后者适合需要动态调整策略的场景,比如根据 key 前缀决定是否重试、或 fallback 到本地缓存。
- 启用需添加依赖:
spring-retry和aspectjweaver,并在主类加@EnableRetry -
@Retryable必须标注在 public 方法上,且该方法不能是 final 或 static;类需由 Spring 容器管理(@Service等) - 重试仅针对指定异常,默认不包括
RuntimeException子类以外的异常,需显式写include = {RedisConnectionFailureException.class} - 避免在事务方法内使用
@Retryable:重试会触发多次事务,可能造成数据不一致
示例:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
@Service
public class UserService {
@Autowired private RedisTemplate<string object> redisTemplate;
@Retryable(
value = {RedisConnectionFailureException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2)
)
public User getUserFromCache(String userId) {
return (User) redisTemplate.opsForValue().get("user:" + userId);
}
}</string>
RetryTemplate 手动构建时的关键参数陷阱
直接 new RetryTemplate() 看似灵活,但容易忽略线程安全与策略耦合问题。它的实例不是线程安全的,若作为单例 bean 注入,多个线程并发调用会导致状态混乱。
- 不要将
RetryTemplate声明为@Bean后直接@Autowired使用——应按场景拆分:读操作用固定延迟策略,写操作用熔断策略(CircuitBreakerRetryPolicy) -
SimpleRetryPolicy.setMaxAttempts(3)设置后,若未同时设置setRetryExceptionClasses(...),默认只对RuntimeException重试,而 Redis 连接异常是NonTransientDataAccessException的子类,会被跳过 -
FixedBackOffPolicy.setBackOffPeriod(1000)是毫秒,别错写成秒;指数退避(ExponentialBackOffPolicy)要设maxInterval防止间隔无限增长 - 生产环境务必配
RecoveryCallback:重试失败后返回默认值或降级数据,而不是让上游持续等待
连接层重试 ≠ 业务层重试,别混淆 Lettuce 自带重连和 Spring Retry
Spring Boot 2.0+ 默认用 Lettuce 客户端,它本身支持连接自动重连(ClientOptions.autoReconnect(true)),但这只是维持连接池活跃,不保证单次命令成功。当节点故障转移、集群拓扑变更时,Lettuce 可能仍返回超时,此时仍需上层重试。
- Lettuce 的
autoReconnect控制 TCP 连接重建,不影响命令执行结果;Spring Retry 控制的是命令调用失败后的补偿行为 - 若同时开启 Lettuce 重连 + Spring Retry,可能造成双重延迟:Lettuce 等 3 秒重连,Spring Retry 再等 1 秒才发起第二次命令
- 真正关键的是配置 Lettuce 的
timeout和topologyRefresh:例如cluster.refresh.period=30000避免因拓扑过期导致误判连接失败
最易被忽略的一点:所有重试都应在 Redis 客户端配置的超时范围内完成。如果 lettuce.timeout 设为 2 秒,而 RetryTemplate 总耗时超过 2 秒(比如 3 次 × 1 秒),第三次重试根本发不出去——它会被客户端直接拒绝。










