使用Spring Cache时 优雅的处理缓存系统无法连接

本文介绍如何在Spring Cache中优雅地处理Redis缓存异常,确保即使缓存系统暂时不可用,应用也能通过数据库提供服务。文章提供了一个具体的配置示例,并讨论了如何使用Hystrix断路器思想来避免数据库过载。

       为了能够满足高并发、快速响应的需求,缓存系统在应用中扮演者越来越重要的作用,它能够减少应用与数据库的交互次数,进而降低数据库的压力。在java应用中使用缓存,很多开发人员会选择使用Spring提供的Cache组件,它提供了抽象的缓存模型,业务程序中只需要添加一个统一的注解,而后端可以通过配置切换不同的缓存系统/组件。

       想象这样一个场景,由于突然的网络不稳定,Redis缓存系统出现了暂时性无法连接,而我们的业务系统又需要继续的提供服务;另一个场景,我们在自己的笔记本上想测试一个功能,但是暂时没有一个可使用的缓存系统。这两个场景下,我们都会看到系统会抛出这样的异常而不能提供服务:

org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
	at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:204)
	at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:348)
	at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:129)
	at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:92)
	at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:79)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:194)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:169)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:157)
	at org.springframework.data.redis.cache.RedisCache.get(RedisCache.java:172)
	at org.springframework.data.redis.cache.RedisCache.get(RedisCache.java:133)
	at org.springframework.cache.interceptor.AbstractCacheInvoker.doGet(AbstractCacheInvoker.java:71)
	at org.springframework.cache.interceptor.CacheAspectSupport.findInCaches(CacheAspectSupport.java:537)
	at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:503)
	at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:389)
	at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:327)
	at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656)

       其实如果不是有极限的性能要求,缓存的暂时不可用不应该使得我们的应用系统不能提供服务,完全可以先直接走数据库,等到可用后再使用缓存。而Spring Cache是给我们提供了这种切入点做这种控制处理的。

       新增一个Cache配置类,继承CachingConfigurerSupport类,实现errorHandler()方法,我们可以覆盖Spring默认的DefaultErrorHandler。为了保证数据一致性,缓存清除方法出错时,我们按原样抛出异常,在查询和插入缓存时如果出现了Redis连接的异常,不做异常抛出处理,这样会继续从数据库中读取/写入数据,不影响正常业务服务,等缓存连接恢复后,又可以正常的提供服务了。

@Configuration
@EnableCaching
public class AppCacheConfigurer extends CachingConfigurerSupport {

    private Logger logger = LoggerFactory.getLogger(AppCacheConfigurer.class);

    public class AppCacheErrorHandler implements CacheErrorHandler{

            @Override
            public void handleCacheGetError(RuntimeException e, Cache cache, Object o) {

                if(e instanceof JedisConnectionException || e instanceof RedisConnectionFailureException){
                    logger.warn("redis has lose connection:",e);
                    return;
                }
                throw e;
            }

            @Override
            public void handleCachePutError(RuntimeException e, Cache cache, Object o, Object o1) {
                if(e instanceof JedisConnectionException || e instanceof RedisConnectionFailureException){
                    logger.warn("redis has lose connection:",e);
                    return;
                }
                throw e;
            }

            @Override
            public void handleCacheEvictError(RuntimeException e, Cache cache, Object o) {
                throw e;
            }

            @Override
            public void handleCacheClearError(RuntimeException e, Cache cache) {
                throw e;
            }
    }

    @Override
    public CacheErrorHandler errorHandler() {
        return new AppCacheErrorHandler();
    }

}

       另外,如果长时间缓存不可用,有可能请求量太大对数据库造成冲击,这里我们也可以借鉴Neflix的Hystrix断路器的思想,设定一个阈值,如果连接失败次数超过这个值,后面的连接就直接抛错了,待连接恢复后,再重置这个值。

转载于:https://my.oschina.net/JasonZhang/blog/994028

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值