Spring Cache with Redis — как изящно обрабатывать или даже пропускать кэширование в случае сбоя подключения к Redis

Я включил кэширование в своем приложении Spring и использую Redis для этой цели. Однако всякий раз, когда происходит сбой соединения, приложение перестает работать, тогда как я думаю, что ему лучше пропустить кэширование и продолжить обычный поток выполнения.

Итак, кто-нибудь знает, как изящно сделать это весной?

Вот исключение, которое я получил.

Caused by: 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

person Peter Bean    schedule 30.12.2014    source источник


Ответы (4)


Начиная с Spring Framework 4.1, существует CacheErrorHandler, который можно реализовать для обработки таких исключений. Обратитесь к javadoc для более подробной информации.

Вы можете зарегистрировать его, если ваш @Configuration класс расширяет CachingConfigurerSupport (см. errorHandler()).

person Stephane Nicoll    schedule 04.01.2015
comment
Большое спасибо за ваше предложение. Мой класс @Configuration уже расширяет SpringBootServletInitializer, поэтому я попытался реализовать CacheErrorHandler напрямую, а не из моего класса @Configuration. Однако, похоже, он не обрабатывает ничего подобного RedisConnectionFailureException. - person Peter Bean; 06.01.2015
comment
Где именно вы видели ссылку на SpringBootServletInitializer? Я написал CachingConfigurerSupport - Для этого нужен Spring Boot 1.2 (Spring 4.1). - person Stephane Nicoll; 06.01.2015
comment
CacheErrorHandler используется только во время операций кэширования. Ошибка подключения к Redis может происходить выше? - person Jaymes Bearden; 30.01.2015
comment
Эм-м-м? Что вы имеете в виду под выше? - person Stephane Nicoll; 04.02.2015
comment
Я думаю, что он, вероятно, имел в виду, что при сбое соединения CacheErrorHandler может не запускаться. - person Peter Bean; 19.09.2015
comment
Было бы неплохо, если бы после предотвращения ошибки сработал какой-то механизм автоматического выключателя. Потому что, если к кэшированному методу будет слишком много вызовов, все ответы будут занимать как минимум время ожидания соединения Redis плюс фактическая работа по получению данных. Я пытался найти какой-то способ сделать это, но у меня не было большого успеха. - person Biga; 27.06.2019
comment
Я сделал то, что сказал @StephaneNicoll, но раньше я получал исключение еще до того, как вызывался метод с пометкой с аннотацией Cacheable, но теперь метод выполняется успешно, и данные извлекаются из БД, а не из Redis, но я получаю исключение после выполнения функции. Это может быть место, где результат помещается в кеш, и он не может подключиться к Redis, поскольку он не работает. Любое решение этого? - person mantri; 11.07.2020
comment
Не уверен, как CacheHandler обрабатывает исключение конфигурации. - person Rocky4Ever; 04.08.2020

CacheErrorHandler, предложенный Stephane Nicoll, полезен. Но это не помогает, когда не удается создать соединение с Redis.

Метод кэширования, такой как @Cacheable, по-прежнему терпит неудачу с RedisConnectionFailureException.

person jeffery.yuan    schedule 22.01.2017
comment
То же самое для меня. Вы нашли какое-нибудь решение? - person mantri; 11.07.2020

Вы можете использовать CacheErrorHandler, как предложил Стефан Николл. Но вы должны обязательно сделать RedisCacheManager transactionAware до false в своей конфигурации Redis Cache Config (чтобы убедиться, что транзакция фиксируется раньше при выполнении части кэширования, а ошибка перехватывается CacheErrorHandler и не дожидайтесь конца выполнения, которое пропускает CacheErrorHandler часть). Функция для установки transactionAware в false выглядит следующим образом:

    @Bean
    public RedisCacheManager redisCacheManager(LettuceConnectionFactory lettuceConnectionFactory) {
        JdkSerializationRedisSerializer redisSerializer = new JdkSerializationRedisSerializer(getClass().getClassLoader());

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(redisDataTTL))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer));

        redisCacheConfiguration.usePrefix();

        RedisCacheManager redisCacheManager = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(lettuceConnectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .build();

        redisCacheManager.setTransactionAware(false);
        return redisCacheManager;
    }
person Yashasvi Raj Pant    schedule 02.02.2021

Подобно тому, что упомянул Стефан, я сделал это, употребив ошибку в блоке try catch. Добавление резервного механизма, при котором, если Redis не работает или данные могут отсутствовать, я извлекаю данные из БД (позже, если я нахожу их, я добавляю те же данные в Redis, если они доступны для поддержания согласованности. )

person Ashish Goel    schedule 17.08.2016