Redis不可用时如何规避RedisConnectionFailureException执行原方法
解决方案
Spring Cache框架原生提供了无侵入扩展点,不需要在业务代码中手动编写try/catch块,即可实现Redis连接故障时自动降级执行原方法逻辑,分为全局配置和方法级配置两种方案:
方案1:全局容错配置(推荐,一次配置所有缓存方法生效)
Spring Cache的缓存操作异常统一由CacheErrorHandler处理,默认实现会直接抛出缓存层异常,你只需要替换为自定义的容错实现,即可让所有缓存操作遇到异常时静默降级,触发原方法执行:
- 编写自定义缓存异常处理器
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.Cache; import org.springframework.cache.interceptor.CacheErrorHandler; public class RedisFallbackCacheErrorHandler implements CacheErrorHandler { private static final Logger log = LoggerFactory.getLogger(RedisFallbackCacheErrorHandler.class); @Override public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) { log.warn("Redis查询异常,key:{}, 原因:{},将降级执行原业务方法", key, exception.getMessage()); // 不抛出异常,Spring会判定为缓存未命中,自动执行原方法 } @Override public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) { log.warn("Redis写入异常,key:{}, 原因:{}", key, exception.getMessage()); // 写入失败不抛出,不影响业务正常返回结果 } @Override public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) { log.warn("Redis删除异常,key:{}, 原因:{}", key, exception.getMessage()); } @Override public void handleCacheClearError(RuntimeException exception, Cache cache) { log.warn("Redis清空异常,缓存名:{}, 原因:{}", cache.getName(), exception.getMessage()); } }
- 注册处理器到Spring缓存配置
import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.interceptor.CacheErrorHandler; import org.springframework.context.annotation.Configuration; @Configuration public class CacheAutoConfig extends CachingConfigurerSupport { @Override public CacheErrorHandler errorHandler() { return new RedisFallbackCacheErrorHandler(); } }
配置完成后,所有标注@Cacheable、@CachePut、@CacheEvict的方法,遇到Redis连接失败、操作超时等缓存层异常时,都不会抛出异常,会自动执行原业务逻辑返回结果。
方案2:方法级容错配置(仅对指定方法生效)
如果不需要全局开启容错,只想给核心查询方法单独配置降级逻辑,可以自定义带容错能力的Redis缓存管理器,在注解上单独指定即可:
- 编写容错版缓存管理器
import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import java.time.Duration; @Configuration @EnableCaching public class FallbackCacheConfig { @Bean public RedisCacheManager fallbackRedisCacheManager(RedisConnectionFactory connectionFactory) { RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofHours(2)); // 按需设置缓存过期时间 RedisCacheWriter faultTolerantWriter = new RedisCacheWriter() { private final RedisCacheWriter defaultWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); @Override public void put(String name, byte[] key, byte[] value, Duration ttl) { try { defaultWriter.put(name, key, value, ttl); } catch (Exception ignored) {} } @Override public byte[] get(String name, byte[] key) { try { return defaultWriter.get(name, key); } catch (Exception e) { // 查询异常返回null,等价于缓存未命中 return null; } } @Override public byte[] putIfAbsent(String name, byte[] key, byte[] value, Duration ttl) { try { return defaultWriter.putIfAbsent(name, key, value, ttl); } catch (Exception e) { return null; } } @Override public void remove(String name, byte[] key) { try { defaultWriter.remove(name, key); } catch (Exception ignored) {} } @Override public void clean(String name, byte[] pattern) { try { defaultWriter.clean(name, pattern); } catch (Exception ignored) {} } }; return new RedisCacheManager(faultTolerantWriter, defaultConfig); } }
- 在需要容错的方法上指定该缓存管理器
@Cacheable(value = GET_PRODUCT_CACHE, cacheManager = "fallbackRedisCacheManager") public ProductDetailMock getProduct(final String productId) { ... }
该方案不会影响其他缓存方法的原有逻辑,仅对指定了fallbackRedisCacheManager的方法生效。
注意事项
- 两种方案均完全不侵入业务代码,不需要修改原有方法的业务逻辑
- 异常降级时建议保留warn级别日志,方便后续排查Redis服务故障,不要完全静默吞掉所有异常
- 如果需要更细粒度的容错策略,还可以结合AOP对特定缓存名、特定异常类型做差异化降级处理
内容的提问来源于stack exchange,提问作者Rubén Morales
相关产品推荐
相关产品推荐

