복붙노트

[REDIS] 봄 레디 스 오류 핸들

REDIS

봄 레디 스 오류 핸들

나는 새 프로젝트에서 내 캐시 구성 요소로 봄 + 레디 스를 사용하고 있습니다. 스프링 설정 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);
}

레디 스 서버가 실행중인 경우 코드가 제대로 실행됩니다. I의 셧다운 레디 스 서버, 그것은 예외가 발생합니다하지만 내 질문은 :

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

레디 스 서버를 연결할 수 없습니다 클라이언트가 왜 예외가 발생하면 내가 물어보고 싶은? 우리는이 같은 시나리오를 config (설정) 할 수 - 캐시 층 (레디 스 서버)를 연결하지 (아마도이 ​​추락 또는 네트워크가 아닌) 할 수 있다면, 그것은 직접 데이터베이스에 연결하고 데이터를 가져해야합니다.

해결법

  1. ==============================

    1.나는 똑같은 문제를 가지고 있었다. 나는 봄 캐싱 주석의 방법으로 캐시 저장소로 레디 스를 사용하여 데이터베이스에 대해 일부 데이터 서비스를 개발하고 있어요. 레디 스 서버를 사용할 수없는 경우에, 나는 서비스가 오히려 예외를 던지는 것보다, 캐시되지 않은 것처럼 계속 작동하고 싶다.

    나는 똑같은 문제를 가지고 있었다. 나는 봄 캐싱 주석의 방법으로 캐시 저장소로 레디 스를 사용하여 데이터베이스에 대해 일부 데이터 서비스를 개발하고 있어요. 레디 스 서버를 사용할 수없는 경우에, 나는 서비스가 오히려 예외를 던지는 것보다, 캐시되지 않은 것처럼 계속 작동하고 싶다.

    첫번째 I에서 정의 CacheErrorHandler, 봄에서 제공하는 메커니즘을 시도했다. 그것은 단지 java.net.ConnectException 블로우 일까지 같은 일을 할 수 있습니다 여전히들이 RuntimeException을 처리하고 있기 때문에, 아주 일을하지 않았다.

    내가 무슨 짓을했는지 말에 그들이 대신 전파 예외의 경고를 기록 할 정도로 몇 실행 () 메소드를 오버라이드 (override), RedisTemplate을 확장합니다. 그것은 해킹 약간 것 같아, 나는 너무 적은 실행 () 메소드 또는 너무 많이 오버라이드 (override) 할 수도 있습니다,하지만 내 모든 테스트 케이스의 매력처럼 작동합니다.

    이 방법의 중요한 운영 측면은 있지만,있다. 레디 스 서버를 사용할 수없는 경우에 당신은 다시 사용할 수 있도록 전에 (항목 청결한) 플러시해야합니다. 그렇지 않으면 때문에 그 동안 발생한 업데이트의 잘못된 데이터가 캐시 항목의 취득을 개시 할 수있는 기회가있다.

    아래는 소스입니다. 그것을 사용하시기 바랍니다. 나는 그것이 도움이되기를 바랍니다.

    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;
            }
        }
    }
    
  2. ==============================

    2.저도 같은 오류가 있습니다. 그리고 두 가지를 추가하여 해결하기 위해 관리 :

    저도 같은 오류가 있습니다. 그리고 두 가지를 추가하여 해결하기 위해 관리 :

    @Configuration
    @ConditionalOnProperty(name = "redis.enabled", havingValue = "true")
    @EnableCaching
    public class RedisCacheConfig extends CachingConfigurerSupport implements CachingConfigurer {
    
        @Value("${redis.host}")
        private String host;
    
        @Value("${redis.port}")
        private Integer port;
    
        @Value("${redis.expiration.timeout}")
        private Integer expirationTimeout;
    
        @Bean
        public JedisConnectionFactory redisConnectionFactory() {
            JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
            redisConnectionFactory.setHostName(host);
            redisConnectionFactory.setPort(port);
            redisConnectionFactory.setTimeout(10);
            return redisConnectionFactory;
        }
    
        @Bean
        public RedisTemplate<String, Set<String>> redisTemplate(@Autowired RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<String, Set<String>> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            return redisTemplate;
        }
    
        @Bean
        public CacheManager cacheManager(@Autowired RedisTemplate redisTemplate) {
            RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
            cacheManager.setDefaultExpiration(expirationTimeout);
            return cacheManager;
        }
    
        @Override
        public CacheErrorHandler errorHandler() {
            return new RedisCacheErrorHandler();
        }
    
        @Slf4j
        public static class RedisCacheErrorHandler implements CacheErrorHandler {
    
            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
                log.info("Unable to get from cache " + cache.getName() + " : " + exception.getMessage());
            }
    
            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
                log.info("Unable to put into cache " + cache.getName() + " : " + exception.getMessage());
            }
    
            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
                log.info("Unable to evict from cache " + cache.getName() + " : " + exception.getMessage());
            }
    
            @Override
            public void handleCacheClearError(RuntimeException exception, Cache cache) {
                log.info("Unable to clean cache " + cache.getName() + " : " + exception.getMessage());
            }
        }
    }
    
  3. ==============================

    3.내가 사용 봄 부팅 v2의 해답을 추가 한 LettuceConnectionFactory

    내가 사용 봄 부팅 v2의 해답을 추가 한 LettuceConnectionFactory

    @Configuration
    @EnableCaching
    public class RedisCacheConfig extends CachingConfigurerSupport implements CachingConfigurer {
    
        @Value("${redis.hostname:localhost}")
        private String redisHost;
    
        @Value("${redis.port:6379}")
        private int redisPort;
    
        @Value("${redis.timeout.secs:1}")
        private int redisTimeoutInSecs;
    
        @Value("${redis.socket.timeout.secs:1}")
        private int redisSocketTimeoutInSecs;
    
        @Value("${redis.ttl.hours:1}")
        private int redisDataTTL;
    
        // @Autowired
        // private ObjectMapper objectMapper;
    
        @Bean
        public LettuceConnectionFactory redisConnectionFactory() {
            // LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
            // .commandTimeout(Duration.ofSeconds(redisConnectionTimeoutInSecs)).shutdownTimeout(Duration.ZERO).build();
            //
            // return new LettuceConnectionFactory(new RedisStandaloneConfiguration(redisHost, redisPort), clientConfig);
    
            final SocketOptions socketOptions = SocketOptions.builder().connectTimeout(Duration.ofSeconds(redisSocketTimeoutInSecs)).build();
    
            final ClientOptions clientOptions = ClientOptions.builder().socketOptions(socketOptions).build();
    
            LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
                    .commandTimeout(Duration.ofSeconds(redisTimeoutInSecs)).clientOptions(clientOptions).build();
            RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration(redisHost, redisPort);
    
            final LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(serverConfig, clientConfig);
            lettuceConnectionFactory.setValidateConnection(true);
            return lettuceConnectionFactory;
    
        }
    
        @Bean
        public RedisTemplate<Object, Object> redisTemplate() {
            RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
            redisTemplate.setConnectionFactory(redisConnectionFactory());
            return redisTemplate;
        }
    
        @Bean
        public RedisCacheManager redisCacheManager(LettuceConnectionFactory lettuceConnectionFactory) {
    
            /**
             * If we want to use JSON Serialized with own object mapper then use the below config snippet
             */
            // RedisCacheConfiguration redisCacheConfiguration =
            // RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()
            // .entryTtl(Duration.ofHours(redisDataTTL)).serializeValuesWith(RedisSerializationContext.SerializationPair
            // .fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)));
    
            RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()
                    .entryTtl(Duration.ofHours(redisDataTTL))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.java()));
    
            redisCacheConfiguration.usePrefix();
    
            RedisCacheManager redisCacheManager = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(lettuceConnectionFactory)
                    .cacheDefaults(redisCacheConfiguration).build();
    
            redisCacheManager.setTransactionAware(true);
            return redisCacheManager;
        }
    
    
        @Override
        public CacheErrorHandler errorHandler() {
            return new RedisCacheErrorHandler();
        }
    

    RedisCacheErrorHandler.java은 아래와 같습니다

    public class RedisCacheErrorHandler implements CacheErrorHandler {
    
        private static final Logger log = LoggerFactory.getLogger(RedisCacheErrorHandler.class);
    
        @Override
        public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
            handleTimeOutException(exception);
            log.info("Unable to get from cache " + cache.getName() + " : " + exception.getMessage());
        }
    
        @Override
        public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
            handleTimeOutException(exception);
            log.info("Unable to put into cache " + cache.getName() + " : " + exception.getMessage());
        }
    
        @Override
        public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
            handleTimeOutException(exception);
            log.info("Unable to evict from cache " + cache.getName() + " : " + exception.getMessage());
        }
    
        @Override
        public void handleCacheClearError(RuntimeException exception, Cache cache) {
            handleTimeOutException(exception);
            log.info("Unable to clean cache " + cache.getName() + " : " + exception.getMessage());
        }
    
        /**
         * We handle redis connection timeout exception , if the exception is handled then it is treated as a cache miss and
         * gets the data from actual storage
         * 
         * @param exception
         */
        private void handleTimeOutException(RuntimeException exception) {
    
            if (exception instanceof RedisCommandTimeoutException)
                return;
        }
    }
    
  4. ==============================

    4.LettuceConnectionFactory 필요가 없습니다. 그냥 CacheConfig가 CachingConfigurerSupport을 확장하는 사용자 정의를 사용하여. 그리고 ErrorHandler를 () 메소드를 오버라이드 (override).

    LettuceConnectionFactory 필요가 없습니다. 그냥 CacheConfig가 CachingConfigurerSupport을 확장하는 사용자 정의를 사용하여. 그리고 ErrorHandler를 () 메소드를 오버라이드 (override).

    당신은 @Tan이 mally 그의 대답에서처럼, 사용자 정의 CacheErrorHandler를 구현해야합니다.

  5. from https://stackoverflow.com/questions/26021991/spring-redis-error-handle by cc-by-sa and MIT license