当前位置: 首页 > 面试题库 >

Spring Redis错误处理

麹高义
2023-03-14
问题内容

我正在使用Spring + Redis作为新项目中的缓存组件。spring config xml文件是:

<!-- Jedis Connection --> 
<bean id="jedisConnectionFactory" 
    class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
    p:host-name="${redis.ip}" p:port="${redis.port}" p:use-pool="${redis.use-pool}" />

<!-- Redis Template -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory" />
    <property name="keySerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
    </property>
    <property name="valueSerializer">  
        <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
    </property> 
</bean>

<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" c:template-ref="redisTemplate"/>

<cache:annotation-driven mode="proxy" proxy-target-class="true" cache-manager="cacheManager" />

用法是

    @Cacheable(value = "cacheManager", key="#userId")
public User getUser(String userId) {
    System.out.println("execute==");
    return userAdminMapper.getUser(userId);
}

我的测试用例是:

@Test
public void testCacheUser2() {
    String id = "test";
    User user = userService.getUser(id);
    System.out.println(user);
    user.setUserCreateDate(new Date());
    userService.updateUser(user);
    User user2 = userService.getUser(id);
    System.out.println(user2);
    User user3 = userService.getUser(id);
    System.out.println(user3);
}

如果Redis服务器正在运行,则代码正在正确运行。但是我的问题是,如果我关闭Redis服务器,它将抛出异常:

org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused: connect
    at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:140)
    at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:229)
    at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:57)
    at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:128)
    at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:91)
    at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:78)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:177)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:152)
    at org.springframework.data.redis.cache.RedisCache.get(RedisCache.java:87)
    at org.springframework.cache.interceptor.CacheAspectSupport.findInCaches(CacheAspectSupport.java:297)
    at org.springframework.cache.interceptor.CacheAspectSupport.findInAnyCaches(CacheAspectSupport.java:287)
    at org.springframework.cache.interceptor.CacheAspectSupport.collectPutRequests(CacheAspectSupport.java:266)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:199)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:178)
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:60)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
    at sg.infolab.common.admin.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$c7f982a7.getUser(<generated>)
    at sg.infolab.admin.test.RedisServiceTest.testCacheUser2(RedisServiceTest.java:35)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused: connect
    at redis.clients.jedis.Connection.connect(Connection.java:150)
    at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:71)
    at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1783)
    at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:137)
    ... 50 more
Caused by: java.net.ConnectException: Connection refused: connect
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
    at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
    at java.net.Socket.connect(Socket.java:529)
    at redis.clients.jedis.Connection.connect(Connection.java:144)
    ... 53 more

我想问一下客户端是否无法连接Redis Server,为什么会抛出异常?我们可以配置这样的方案吗?-如果缓存层(Redis
Server)无法连接(可能是崩溃或网络未启动),它应该直接连接到数据库并获取数据。


问题答案:

我有同样的问题。我正在通过Spring
Caching注释将Redis用作缓存存储区,从而针对数据库开发一些数据服务。如果Redis服务器不可用,我希望这些服务能够像未缓存一样继续运行,而不是抛出异常。

最初,我尝试了一个自定义的CacheErrorHandler,这是Spring提供的一种机制。它并不是很有效,因为它只处理RuntimeExceptions,并且仍然让java.net.ConnectException之类的东西炸毁。

最后,我所做的是扩展RedisTemplate,覆盖了一些execute()方法,以便它们记录警告而不是传播异常。似乎有点hack,我可能重写了太少的execute()方法或太多了,但是在我所有的测试用例中,它都像一个魅力。

但是,此方法有一个重要的操作方面。如果Redis服务器不可用,则必须刷新它(清除条目),然后才能再次使用它。否则,由于同时发生更新,您可能会开始检索数据不正确的缓存条目。

以下是来源。随意使用它。希望对您有所帮助。

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;


/**
 * An extension of RedisTemplate that logs exceptions instead of letting them propagate.
 * If the Redis server is unavailable, cache operations are always a "miss" and data is fetched from the database.
 */
public class LoggingRedisTemplate<K, V> extends RedisTemplate<K, V> {

    private static final Logger logger = LoggerFactory.getLogger(LoggingRedisTemplate.class);


    @Override
    public <T> T execute(final RedisCallback<T> action, final boolean exposeConnection, final boolean pipeline) {
        try {
            return super.execute(action, exposeConnection, pipeline);
        }
        catch(final Throwable t) {
            logger.warn("Error executing cache operation: {}", t.getMessage());
            return null;
        }
    }


    @Override
    public <T> T execute(final RedisScript<T> script, final List<K> keys, final Object... args) {
        try {
            return super.execute(script, keys, args);
        }
        catch(final Throwable t) {
            logger.warn("Error executing cache operation: {}", t.getMessage());
            return null;
        }
    }


    @Override
    public <T> T execute(final RedisScript<T> script, final RedisSerializer<?> argsSerializer, final RedisSerializer<T> resultSerializer, final List<K> keys, final Object... args) {
        try {
            return super.execute(script, argsSerializer, resultSerializer, keys, args);
        }
        catch(final Throwable t) {
            logger.warn("Error executing cache operation: {}", t.getMessage());
            return null;
        }
    }


    @Override
    public <T> T execute(final SessionCallback<T> session) {
        try {
            return super.execute(session);
        }
        catch(final Throwable t) {
            logger.warn("Error executing cache operation: {}", t.getMessage());
            return null;
        }
    }
}


 类似资料:
  • 通过对错误类型实现 Display 和 From,我们能够利用上绝大部分标准库错误处理工具。然而,我们遗漏了一个功能:轻松 Box 我们错误类型的能力。 标准库会自动通过 Form 将任意实现了 Error trait 的类型转换成 trait 对象 Box<Error> 的类型(原文:The std library automatically converts any type that imp

  • 错误处理(error handling)是处理可能发生失败情况的过程。例如读取一个文件失败,然后继续使用这个失效的输入显然是有问题的。错误处理允许我们以一种显式的方式来发现并处理这类错误,避免了其余代码发生潜在的问题。 有关错误处理的更多内容,可参考官方文档的错误处理的章节。

  • 处理一个 RESTful API 请求时, 如果有一个用户请求错误或服务器发生意外时, 你可以简单地抛出一个异常来通知用户出错了。 如果你能找出错误的原因 (例如,所请求的资源不存在),你应该 考虑抛出一个适当的HTTP状态代码的异常 (例如, yii\web\NotFoundHttpException意味着一个404 HTTP状态代码)。 Yii 将通过HTTP状态码和文本发送相应的响应。 它还

  • Yii 内置了一个error handler错误处理器,它使错误处理更方便, Yii错误处理器做以下工作来提升错误处理效果: 所有非致命PHP错误(如,警告,提示)会转换成可获取异常; 异常和致命的PHP错误会被显示, 在调试模式会显示详细的函数调用栈和源代码行数。 支持使用专用的 控制器操作 来显示错误; 支持不同的错误响应格式; error handler 错误处理器默认启用, 可通过在应用的

  • 介绍 当你启动一个新的 Laravel 项目时,错误及异常处理是已为你配置好了的。App\Exceptions\Handler 类负责记录应用程序触发的所有异常并呈现给用户。在本文档中,我们将深入探讨这个类。 配置 config/app.php 配置文件中的 debug 选项决定了对于一个错误实际上将显示多少信息给用户。默认情况下,该选项的设置将遵照存储在 .env 文件中的 APP_DEBUG

  • 错误处理 错误抛出是个好东西!这使得你能够成功定位运行状态中的程序产生错误的位置。 别忘了捕获错误 对捕获的错误不做任何处理是没有意义的。 代码中 try/catch 的意味着你认为这里可能出现一些错误,你应该对这些可能的错误存在相应的处理方案。 反例: try { functionThatMightThrow(); } catch (error) { console.log(error)

  • Lavas 的错误处理采用中间件的方式,分别在 express 和 koa 两种模式下添加中间件,用以捕获之后抛出的一切错误,带上错误信息一并跳转到配置好的错误处理路径。因此,除了配置错误路径之外,还需要编写一个错误处理页面,供跳转使用。 错误配置 Lavas 的错误处理相关的配置比较简洁,在 /lavas.config.js 中找到 errorHandler 对象,如下: // ... erro

  • 错误处理是相应和接收来自你程序中错误条件的过程。Swift 给运行时可恢复错误的抛出、捕获、传递和操纵提供了一类支持。 有些函数和方法不能保证总能完全执行或者产生有用的输出。可选项用来表示不存在值,但是当函数错误,能够了解到什么导致了错误将会变得很有用处,这样你的代码就能根据错误来响应了。 举例来说,假设一个阅读和处理来自硬盘上文件数据的任务。这种情况下有很多种导致任务失败的方法,目录中文件不存在