Ho avuto lo stesso problema. Sto sviluppando alcuni servizi dati su un database, utilizzando Redis come archivio cache tramite le annotazioni di Spring Caching. Se il server Redis diventa non disponibile, voglio che i servizi continuino a funzionare come se non fossero memorizzati nella cache, invece di generare eccezioni.
All'inizio ho provato un CacheErrorHandler personalizzato, un meccanismo fornito da Spring. Non ha funzionato del tutto, perché gestisce solo RuntimeExceptions e consente comunque a cose come java.net.ConnectException di far saltare in aria le cose.
Alla fine quello che ho fatto è estendere RedisTemplate, sovrascrivendo alcuni metodi execute() in modo che registrino avvisi invece di propagare eccezioni. Sembra un po' un trucco e potrei aver sovrascritto troppi pochi metodi execute() o troppi, ma funziona come un incantesimo in tutti i miei casi di test.
C'è un aspetto operativo importante in questo approccio, però. Se il server Redis diventa non disponibile, è necessario svuotarlo (pulire le voci) prima di renderlo nuovamente disponibile. Altrimenti c'è la possibilità che tu possa iniziare a recuperare le voci della cache con dati errati a causa di aggiornamenti che si sono verificati nel frattempo.
Di seguito la fonte. Sentiti libero di usarlo. Spero che aiuti.
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;
}
}
}