Spring @Cacheable redis异常不影响正常业务方案

 更新时间:2021年2月19日 11:09  点击:1708

背景

项目中,使用@Cacheable进行数据缓存。发现:当redis宕机之后,@Cacheable注解的方法并未进行缓存冲突,而是直接抛出异常。而这样的异常会导致服务不可用。

原因分析

我们是通过@EnableCaching进行缓存启用的,因此可以先看@EnableCaching的相关注释

通过@EnableCaching的类注释可发现,spring cache的核心配置接口为:org.springframework.cache.annotation.CachingConfigurer

/**
 * Interface to be implemented by @{@link org.springframework.context.annotation.Configuration
 * Configuration} classes annotated with @{@link EnableCaching} that wish or need to
 * specify explicitly how caches are resolved and how keys are generated for annotation-driven
 * cache management. Consider extending {@link CachingConfigurerSupport}, which provides a
 * stub implementation of all interface methods.
 *
 * <p>See @{@link EnableCaching} for general examples and context; see
 * {@link #cacheManager()}, {@link #cacheResolver()} and {@link #keyGenerator()}
 * for detailed instructions.
 *
 * @author Chris Beams
 * @author Stephane Nicoll
 * @since 3.1
 * @see EnableCaching
 * @see CachingConfigurerSupport
 */
public interface CachingConfigurer {

 /**
 * Return the cache manager bean to use for annotation-driven cache
 * management. A default {@link CacheResolver} will be initialized
 * behind the scenes with this cache manager. For more fine-grained
 * management of the cache resolution, consider setting the
 * {@link CacheResolver} directly.
 * <p>Implementations must explicitly declare
 * {@link org.springframework.context.annotation.Bean @Bean}, e.g.
 * <pre class="code">
 * Configuration
 * EnableCaching
 * public class AppConfig extends CachingConfigurerSupport {
 *  Bean // important!
 *  Override
 *  public CacheManager cacheManager() {
 *   // configure and return CacheManager instance
 *  }
 *  // ...
 * }
 * </pre>
 * See @{@link EnableCaching} for more complete examples.
 */
 CacheManager cacheManager();

 /**
 * Return the {@link CacheResolver} bean to use to resolve regular caches for
 * annotation-driven cache management. This is an alternative and more powerful
 * option of specifying the {@link CacheManager} to use.
 * <p>If both a {@link #cacheManager()} and {@code #cacheResolver()} are set,
 * the cache manager is ignored.
 * <p>Implementations must explicitly declare
 * {@link org.springframework.context.annotation.Bean @Bean}, e.g.
 * <pre class="code">
 * Configuration
 * EnableCaching
 * public class AppConfig extends CachingConfigurerSupport {
 *  Bean // important!
 *  Override
 *  public CacheResolver cacheResolver() {
 *   // configure and return CacheResolver instance
 *  }
 *  // ...
 * }
 * </pre>
 * See {@link EnableCaching} for more complete examples.
 */
 CacheResolver cacheResolver();

 /**
 * Return the key generator bean to use for annotation-driven cache management.
 * Implementations must explicitly declare
 * {@link org.springframework.context.annotation.Bean @Bean}, e.g.
 * <pre class="code">
 * Configuration
 * EnableCaching
 * public class AppConfig extends CachingConfigurerSupport {
 *  Bean // important!
 *  Override
 *  public KeyGenerator keyGenerator() {
 *   // configure and return KeyGenerator instance
 *  }
 *  // ...
 * }
 * </pre>
 * See @{@link EnableCaching} for more complete examples.
 */
 KeyGenerator keyGenerator();

 /**
 * Return the {@link CacheErrorHandler} to use to handle cache-related errors.
 * <p>By default,{@link org.springframework.cache.interceptor.SimpleCacheErrorHandler}
 * is used and simply throws the exception back at the client.
 * <p>Implementations must explicitly declare
 * {@link org.springframework.context.annotation.Bean @Bean}, e.g.
 * <pre class="code">
 * Configuration
 * EnableCaching
 * public class AppConfig extends CachingConfigurerSupport {
 *  Bean // important!
 *  Override
 *  public CacheErrorHandler errorHandler() {
 *   // configure and return CacheErrorHandler instance
 *  }
 *  // ...
 * }
 * </pre>
 * See @{@link EnableCaching} for more complete examples.
 */
 CacheErrorHandler errorHandler();

}

该接口errorHandler方法可配置异常的处理方式。通过该方法上的注释可以发现,默认的CacheErrorHandler实现类是org.springframework.cache.interceptor.SimpleCacheErrorHandler

/**
 * A simple {@link CacheErrorHandler} that does not handle the
 * exception at all, simply throwing it back at the client.
 *
 * @author Stephane Nicoll
 * @since 4.1
 */
public class SimpleCacheErrorHandler implements CacheErrorHandler {

 @Override
 public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
 throw exception;
 }

 @Override
 public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
 throw exception;
 }

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

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

SimpleCacheErrorHandler类注释上说明的很清楚:对cache的异常不做任何处理,直接将该异常抛给客户端。因此默认的情况下,redis服务器异常后,直接就阻断了正常业务

解决方案

通过上面的分析可知,我们可以通过自定义CacheErrorHandler来干预@Cacheable的异常处理逻辑。具体代码如下:

public class RedisConfig extends CachingConfigurerSupport {

  /**
   * redis数据操作异常处理。该方法处理逻辑:在日志中打印出错误信息,但是放行。
   * 保证redis服务器出现连接等问题的时候不影响程序的正常运行
   */
  @Override
  public CacheErrorHandler errorHandler() {
    return new CacheErrorHandler() {
      @Override
      public void handleCachePutError(RuntimeException exception, Cache cache,
                      Object key, Object value) {
        handleRedisErrorException(exception, key);
      }

      @Override
      public void handleCacheGetError(RuntimeException exception, Cache cache,
                      Object key) {
        handleRedisErrorException(exception, key);
      }

      @Override
      public void handleCacheEvictError(RuntimeException exception, Cache cache,
                       Object key) {
        handleRedisErrorException(exception, key);
      }

      @Override
      public void handleCacheClearError(RuntimeException exception, Cache cache) {
        handleRedisErrorException(exception, null);
      }
    };
  }

  protected void handleRedisErrorException(RuntimeException exception, Object key) {
    log.error("redis异常:key=[{}]", key, exception);
  }
}

到此这篇关于Spring @Cacheable redis异常不影响正常业务方案的文章就介绍到这了,更多相关Spring @Cacheable redis异常内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

[!--infotagslink--]

相关文章

  • Spring AOP 对象内部方法间的嵌套调用方式

    这篇文章主要介绍了Spring AOP 对象内部方法间的嵌套调用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-08-29
  • Spring Cloud 中@FeignClient注解中的contextId属性详解

    这篇文章主要介绍了Spring Cloud 中@FeignClient注解中的contextId属性详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-25
  • 详解如何清理redis集群的所有数据

    这篇文章主要介绍了详解如何清理redis集群的所有数据,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-18
  • Redis连接池配置及初始化实现

    这篇文章主要介绍了Redis连接池配置及初始化实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-29
  • Springboot如何实现Web系统License授权认证

    这篇文章主要介绍了Springboot如何实现Web系统License授权认证,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-05-28
  • 详解redis desktop manager安装及连接方式

    这篇文章主要介绍了redis desktop manager安装及连接方式,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-15
  • 如何在Spring WebFlux的任何地方获取Request对象

    这篇文章主要介绍了如何在Spring WebFlux的任何地方获取Request对象,帮助大家更好的理解和使用springboot框架,感兴趣的朋友可以了解下...2021-01-26
  • 浅谈redis key值内存消耗以及性能影响

    这篇文章主要介绍了浅谈redis key值内存消耗以及性能影响,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-07
  • 详解SpringCloudGateway内存泄漏问题

    这篇文章主要介绍了详解SpringCloudGateway内存泄漏问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-07-16
  • lua读取redis数据的null判断示例代码

    最近在工作中遇到了一个问题,通过查找相关资料才得知原因是因为返回结果的问题,下面这篇文章主要给大家介绍了关于lua读取redis数据的null判断的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下...2020-06-30
  • SpringBoot集成Redis实现消息队列的方法

    这篇文章主要介绍了SpringBoot集成Redis实现消息队列的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-10
  • redis setIfAbsent和setnx的区别与使用说明

    这篇文章主要介绍了redis setIfAbsent和setnx的区别与使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-08-04
  • Redis的Expire与Setex区别说明

    这篇文章主要介绍了Redis的Expire与Setex区别说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-15
  • Spring为什么不推荐使用@Autowired注解详析

    @Autowired 注解的主要功能就是完成自动注入,使用也非常简单,但这篇文章主要给大家介绍了关于Spring为什么不推荐使用@Autowired注解的相关资料,需要的朋友可以参考下...2021-11-03
  • Springboot如何使用mybatis实现拦截SQL分页

    这篇文章主要介绍了Springboot使用mybatis实现拦截SQL分页,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-06-19
  • SpringMVC文件上传原理及实现过程解析

    这篇文章主要介绍了SpringMVC文件上传原理及实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-15
  • 查看Redis内存信息的命令

    Redis 是一个开源、高性能的Key-Value数据库,被广泛应用在服务器各种场景中。本文介绍几个查看Redis内存信息的命令,包括常用的info memory、info keyspace、bigkeys等。...2021-01-15
  • Spring Data JPA 关键字Exists的用法说明

    这篇文章主要介绍了Spring Data JPA 关键字Exists的用法说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-10
  • Redis的持久化方案详解

    在本篇文章里小编给大家整理的是关于Redis的持久化方案详解,有兴趣的朋友们可以参考下。...2021-01-15
  • JAVA中 redisTemplate 和 jedis的配合使用操作

    这篇文章主要介绍了JAVA中 redisTemplate 和 jedis的配合使用操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-13