#spring-boot #redis
#spring-boot #redis
Вопрос:
У меня есть приложение, развернутое в AWS EC2, некоторое время (редко), я не могу подключиться и выполнить какую-либо команду в redis, я расследую основную причину этой проблемы.
Я использую Spring boot Redis (Elasticcache).
Я использую оболочку для перехвата любого исключения для продолжения процесса запроса.
Моя оболочка:
class RedisCacheWrapper implements Cache {
private final Cache delegate;
public RedisCacheWrapper(Cache redisCache) {
Assert.notNull(redisCache, "delegate cache must not be null");
this.delegate = redisCache;
}
@Override
public String getName() {
try {
return delegate.getName();
} catch (Exception e) {
return handleException(e);
}
}
@Override
public Object getNativeCache() {
try {
return delegate.getNativeCache();
} catch (Exception e) {
return handleException(e);
}
}
@Override
public ValueWrapper get(Object key) {
try {
return delegate.get(key);
} catch (Exception e) {
return handleException(e);
}
}
@Override
public <T> T get(Object o, Class<T> type) {
try {
return delegate.get(o, type);
} catch (Exception e) {
return handleException(e);
}
}
@Override
public <T> T get(Object o, Callable<T> callable) {
try {
return delegate.get(o, callable);
} catch (Exception e) {
return handleException(e);
}
}
@Override
public void put(Object key, Object value) {
try {
delegate.put(key, value);
} catch (Exception e) {
handleException(e);
}
}
@Override
public ValueWrapper putIfAbsent(Object o, Object o1) {
try {
return delegate.putIfAbsent(o, o1);
} catch (Exception e) {
return handleException(e);
}
}
@Override
public void evict(Object o) {
try {
delegate.evict(o);
} catch (Exception e) {
handleException(e);
}
}
@Override
public void clear() {
try {
delegate.clear();
} catch (Exception e) {
handleException(e);
}
}
private <T> T handleException(Exception e) {
log.error("handleException", e);
return null;
}}
В моей конфигурации redis я установил время ожидания равным 1 сек. Итак, когда connect / command не выполняется через 1 секунду, redis выдает исключение, подобное этому:
Caused by: io.lettuce.core.RedisCommandTimeoutException: Command timed out
Мои сомнения:
Есть хороший способ временно отключить кэш (без какого-либо развертывания), в то время как redis не подходит? Например: использование прерывания цепи?
Я думаю, сделайте это:
@Cacheable()
myMethodCached(){
myRealMethod();
}
myRealMethod(){}
Поместите «myMethodCached» в команду HystrixCommand, если время ожидания истекло, то резервный метод выполняется без использования redis.
Проблема с этим подходом заключается в том, что мне нужно создать «запасной вариант» для всех методов, использующих кеш, я хотел бы «отключить» глобально (весь кеш будет пропущен).
Есть ли хорошее решение для «отключения» redis на определенный период?
Ответ №1:
Если вы используете Spring Data Redis, вы можете использовать поддержку Spring для обработки этих временных сбоев и исключений с помощью пользовательского обработчика исключений.
Код:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Рекомендуем установить тайм-аут ниже, чем по умолчанию (60000):
spring.cache.type=redis
spring.redis.timeout=100
Затем создайте пользовательский обработчик ошибок в контексте Spring:
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.context.annotation.Configuration;
@Slf4j
@EnableCaching
@Configuration
public class CacheConfiguration extends CachingConfigurerSupport {
@Override
public CacheErrorHandler errorHandler() {
return new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
log.info("Failure getting from cache: " cache.getName() ", exception: " exception.toString());
}
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
log.info("Failure putting into cache: " cache.getName() ", exception: " exception.toString());
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
log.info("Failure evicting from cache: " cache.getName() ", exception: " exception.toString());
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
log.info("Failure clearing cache: " cache.getName() ", exception: " exception.toString());
}
};
}
}
Spring должен обнаружить сбой через 100 миллисекунд и выполнить резервное копирование для извлечения данных, полученных с помощью @Cacheable
аннотированных методов, как если бы был промах кэша. И всякий раз, когда кэш восстанавливается, Spring снова начнет извлекать данные из кэша.
Комментарии:
1. у меня работало с использованием spring boot 2.6.4
2. Протестировано
spring-boot@2.6.6
и работает хорошо, но только если экземпляр Redis все еще доступен. Если вы не можете подключиться к нему, клиент Lettuce будет жаловаться, и вызовы будут отключены (сio.lettuce.core.RedisCommandTimeoutException
выдачей исключения). В этом случае обработчик ошибок никогда не достигается, что является позором, потому что это означает, что у вас должен быть другой запасной вариант на случай, если экземпляр полностью недоступен.3. К сожалению, проблема, с которой я столкнулся, заключалась в том, что у меня также были Spring sessions (
spring-session-data-redis
), сохраненные в экземпляре Redis.