让我们稍微简化一下。您的应用程序使用缓存(使用 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 核心的缓存抽象中确定缓存“未命中”的所有重要因素是返回的值为 null。因此,Spring Caching Infrastructure 将继续调用实际的 Service 方法(即 getCustomer)。请记住,在 getCustomerRepo().load(customerId) 调用的返回上,您还需要处理 Spring 的 Caching Infrastructure 现在尝试缓存该值的情况。
本着保持简单的精神,我们将不用 AOP,但您也应该能够使用 AOP 来实现这一点(您的选择)。
您(应该)需要的只是一个扩展 SDR CacheManager implementation 的“自定义”RedisCacheManager,类似于...
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 存在问题,以便您可以适当地处理异常.但是,如果您在检查时确定 Exception 与连接问题有关,您可以返回“null”,让 Spring Caching Infrastructure 像缓存“未命中”一样继续进行(即错误的 Redis 连接 == 缓存未命中,在这种情况下)。
我知道这样的事情应该可以帮助您解决问题,因为我为 GemFire 和 Pivotal 的一位客户构建了一个类似的“自定义”CacheManager 实现原型。在那个特定的 UC 中,缓存“未命中”必须由应用程序域对象的“过期版本”触发,其中生产环境中混合了新旧应用程序客户端通过 Spring 的缓存抽象连接到 GemFire。例如,应用程序域对象字段会在较新版本的应用程序中发生变化。
无论如何,希望这对您有所帮助或给您更多想法。
干杯!