私もまったく同じ問題を抱えていました。 Spring Cachingアノテーションを使用してRedisをキャッシュストアとして使用して、データベースに対していくつかのデータサービスを開発しています。 Redisサーバーが使用できなくなった場合でも、例外をスローするのではなく、キャッシュされていないかのようにサービスを動作させ続けたいと思います。
最初に、Springが提供するメカニズムであるカスタムCacheErrorHandlerを試しました。 RuntimeExceptionsのみを処理し、それでもjava.net.ConnectExceptionのようなものが爆発するため、完全には機能しませんでした。
最後に、私が行ったことは、RedisTemplateを拡張し、いくつかのexecute()メソッドをオーバーライドして、例外を伝播する代わりに警告をログに記録するようにすることです。ちょっとしたハックのようで、オーバーライドした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;
}
}
}