Résumons un peu cela. Votre application utilise la mise en cache (implémentée avec Redis). Si la connexion Redis est obsolète/fermée ou autre, vous souhaitez que l'application contourne la mise en cache et (vraisemblablement) aille directement à un magasin de données sous-jacent (par exemple, RDBMS). La logique du service d'application peut ressembler à...
@Service
class CustomerService ... {
@Autowired
private CustomerRepository customerRepo;
protected CustomerRepository getCustomerRepo() {
Assert.notNull(customerRepo, "The CustomerRepository was not initialized!");
return customerRepo;
}
@Cacheable(value = "Customers")
public Customer getCustomer(Long customerId) {
return getCustomerRepo().load(customerId);
}
...
}
Tout ce qui compte dans l'abstraction de cache de Spring core pour déterminer un "échec" du cache, c'est que la valeur renvoyée est nulle. En tant que tel, Spring Caching Infrastructure procédera ensuite à l'appel de la méthode de service réelle (c'est-à-dire getCustomer). Gardez à l'esprit qu'au retour de l'appel getCustomerRepo().load(customerId), vous devez également gérer le cas où l'infrastructure de mise en cache de Spring tente maintenant de mettre en cache la valeur.
Dans l'esprit de garder les choses simples , nous nous passerons d'AOP, mais vous devriez également pouvoir y parvenir en utilisant AOP (votre choix).
Tout ce dont vous (devriez) avoir besoin est un RedisCacheManager "personnalisé" étendant l'implémentation du SDR CacheManager, quelque chose comme...
package example;
import org.springframework.cache.Cache;
import org.springframework.data.redis.cache.RedisCacheManager;
...
class MyCustomRedisCacheManager extends RedisCacheManager {
public MyCustomerRedisCacheManager(RedisTemplate redisTemplate) {
super(redisTemplate);
}
@Override
public Cache getCache(String name) {
return new RedisCacheWrapper(super.getCache(name));
}
protected static class RedisCacheWrapper implements Cache {
private final Cache delegate;
public RedisCacheWrapper(Cache redisCache) {
Assert.notNull(redisCache, "'delegate' must not be null");
this.delegate = redisCache;
}
@Override
public Cache.ValueWrapper get(Object key) {
try {
delegate.get(key);
}
catch (Exception e) {
return handleErrors(e);
}
}
@Override
public void put(Object key, Object value) {
try {
delegate.put(key, value);
}
catch (Exception e) {
handleErrors(e);
}
}
// implement clear(), evict(key), get(key, type), getName(), getNativeCache(), putIfAbsent(key, value) accordingly (delegating to the delegate).
protected <T> T handleErrors(Exception e) throws Exception {
if (e instanceof <some RedisConnection Exception type>) {
// log the connection problem
return null;
}
else if (<something different>) { // act appropriately }
...
else {
throw e;
}
}
}
}
Donc, si Redis n'est pas disponible, le mieux que vous puissiez faire est peut-être de consigner le problème et de laisser l'invocation du service se produire. De toute évidence, cela entravera les performances, mais au moins cela fera prendre conscience qu'un problème existe. De toute évidence, cela pourrait être lié à un système de notification plus robuste, mais c'est un exemple grossier des possibilités. L'important est que votre service reste disponible alors que les autres services (par exemple Redis) dont dépend le service d'application peuvent avoir échoué.
Dans cette implémentation (par rapport à mon explication précédente), j'ai choisi de déléguer à l'implémentation réelle sous-jacente de RedisCache pour laisser l'exception se produire, sachant très bien qu'un problème avec Redis existe, et pour que vous puissiez gérer l'exception de manière appropriée. Cependant, si vous êtes certain que l'exception est liée à un problème de connexion lors de l'inspection, vous pouvez renvoyer "null" pour laisser Spring Caching Infrastructure procéder comme s'il s'agissait d'un cache "manqué" (c'est-à-dire mauvaise connexion Redis ==Cache manqué, dans ce cas).
Je sais que quelque chose comme ça devrait résoudre votre problème car j'ai construit un prototype similaire d'une implémentation "personnalisée" de CacheManager pour GemFire et l'un des clients de Pivotal. Dans cet UC particulier, le "manquement" du cache devait être déclenché par une "version obsolète" de l'objet de domaine d'application où la production comportait un mélange de clients d'application plus récents et plus anciens se connectant à GemFire via Spring's Caching Abstraction. Les champs de l'objet de domaine d'application changeraient dans les nouvelles versions de l'application par exemple.
Quoi qu'il en soit, j'espère que cela vous aidera ou vous donnera plus d'idées.
Santé !