Vamos reduzir isso um pouco. Seu aplicativo usa cache (implementado com Redis). Se a conexão Redis estiver obsoleta/fechada ou não, você deseja que o aplicativo ignore o cache e (presumivelmente) vá diretamente para um armazenamento de dados subjacente (por exemplo, RDBMS). A lógica do serviço do aplicativo pode ser semelhante a...
@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);
}
...
}
Tudo o que importa na abstração de cache do Spring core para verificar uma "falta" de cache é que o valor retornado é nulo. Como tal, o Spring Caching Infrastructure continuará chamando o método Service real (ou seja, getCustomer). Tenha em mente que no retorno da chamada getCustomerRepo().load(customerId), você também precisa lidar com o caso em que a Caching Infrastructure do Spring tenta agora armazenar o valor em cache.
No espírito de manter as coisas simples , faremos sem AOP, mas você também deve conseguir isso usando AOP (sua escolha).
Tudo o que você (deve) precisar é de um RedisCacheManager "personalizado" estendendo a implementação do SDR CacheManager, algo como ...
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;
}
}
}
}
Portanto, se o Redis não estiver disponível, talvez o melhor que você possa fazer seja registrar o problema e prosseguir para permitir que a invocação do serviço aconteça. Claramente, isso prejudicará o desempenho, mas pelo menos aumentará a conscientização de que existe um problema. Claramente, isso poderia estar vinculado a um sistema de notificação mais robusto, mas é um exemplo grosseiro das possibilidades. O importante é que seu Serviço permaneça disponível enquanto os outros serviços (por exemplo, Redis) dos quais o serviço de aplicativo depende podem ter falhado.
Nesta implementação (vs. minha explicação anterior), optei por delegar à implementação real do RedisCache subjacente para permitir que a exceção ocorra, sabendo muito bem que existe um problema com o Redis e para que você possa lidar com a exceção adequadamente. No entanto, se você tiver certeza de que a exceção está relacionada a um problema de conexão na inspeção, você pode retornar "null" para permitir que a infraestrutura de cache do Spring continue como se fosse uma "falta" de cache (ou seja, conexão Redis ruim ==falha de cache, nesse caso).
Eu sei que algo assim deve ajudar seu problema, pois construí um protótipo semelhante de uma implementação "personalizada" do CacheManager para o GemFire e um dos clientes da Pivotal. Nesse UC em particular, a "falta" do Cache teve que ser acionada por uma "versão desatualizada" do objeto de domínio do aplicativo em que a produção tinha uma mistura de clientes de aplicativos mais novos e antigos se conectando ao GemFire por meio do Caching Abstraction do Spring. Os campos do objeto de domínio do aplicativo seriam alterados em versões mais recentes do aplicativo, por exemplo.
De qualquer forma, espero que isso ajude ou dê mais ideias.
Saúde!