Нека да сварим това малко. Вашето приложение използва кеширане (реализирано с Redis). Ако връзката Redis е остаряла/затворена или по друг начин, тогава искате приложението да заобиколи кеширането и (вероятно) да отиде директно към основното хранилище на данни (например RDBMS). Логиката на услугата на приложението може да изглежда подобно на...
@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);
}
...
}
Всичко, което има значение в абстракцията за кеширане на ядрото на Spring, за да се установи „пропускане“ на кеша, е, че върнатата стойност е нула. Като такава, Spring Caching Infrastructure ще продължи с извикването на действителния метод на услугата (т.е. getCustomer). Имайте предвид, че при връщането на извикването getCustomerRepo().load(customerId) трябва да се справите и със случая, когато инфраструктурата за кеширане на Spring се опитва да кешира стойността.
В духа на просто , ние ще направим без AOP, но трябва да можете да постигнете това и с AOP (по ваш избор).
Всичко, от което (трябва) се нуждаете, е „персонализиран“ RedisCacheManager, разширяващ реализацията на SDR CacheManager, нещо като...
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;
}
}
}
}
Така че, ако Redis не е наличен, може би най-доброто, което можете да направите, е да регистрирате проблема и да продължите, за да оставите извикването на услугата. Ясно е, че това ще попречи на производителността, но поне ще повиши осведомеността, че съществува проблем. Ясно е, че това може да бъде обвързано с по-стабилна система за уведомяване, но е груб пример за възможностите. Важното е, че вашата услуга остава достъпна, докато другите услуги (напр. Redis), от които зависи услугата на приложението, може да са се провалили.
В тази реализация (в сравнение с моето предишно обяснение) избрах да делегирам на основната, действителна реализация на RedisCache, за да позволя на изключението да се случи, след което знаейки добре, че съществува проблем с Redis и за да можете да се справите с изключението по подходящ начин. Въпреки това, ако сте сигурни, че изключението е свързано с проблем с връзката при проверка, можете да върнете "null", за да позволите на Spring Caching Infrastructure да продължи, сякаш е "пропускане" на кеша (т.е. лоша връзка с Redis ==пропускане на кеш, в този случай).
Знам, че нещо подобно трябва да помогне на проблема ви, тъй като създадох подобен прототип на "персонализирана" реализация на CacheManager за GemFire и един от клиентите на Pivotal. В този конкретен UC „пропускането“ на кеша трябваше да бъде задействано от „неактуална версия“ на обекта на домейна на приложението, където производството имаше смесица от по-нови и по-стари клиенти на приложения, свързващи се с GemFire чрез абстракцията за кеширане на Spring. Обектните полета на домейна на приложението биха се променили в по-новите версии на приложението например.
Както и да е, надявам се това да ви помогне или да ви даде повече идеи.
Наздраве!