首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如果Redis连接失败,如何在运行时禁用redis缓存

如果Redis连接失败,如何在运行时禁用redis缓存
EN

Stack Overflow用户
提问于 2015-03-12 15:09:55
回答 7查看 15.4K关注 0票数 16

我们有rest api应用程序。我们使用redis进行API响应缓存和内部方法缓存。如果是redis连接,那么它就会使我们的API宕机。如果redis连接失败或任何异常,我们希望绕过redis缓存,而不是关闭我们的API。有一个接口CacheErrorHandler,但它处理redis get set操作失败,而不是redis连接问题。我们使用的是Spring 4.1.2。

EN

回答 7

Stack Overflow用户

发布于 2015-03-27 01:55:46

让我们把这个问题简而言之。您的应用程序使用缓存(使用Redis实现)。如果Redis连接是陈旧的/关闭的或其他,那么您希望应用程序绕过缓存并(假设)直接转到底层数据存储(例如RDBMS)。应用程序服务逻辑可能类似于...

代码语言:javascript
运行
复制
@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 core的缓存抽象中,要确定Cache“未命中”,最重要的是返回的值为空。因此,Spring Caching Infrastructure将继续调用实际的服务方法(即getCustomer)。请记住,在返回getCustomerRepo().load(customerId)调用时,您还需要处理Spring的缓存基础结构现在尝试缓存值的情况。

本着保持简单的精神,我们将不使用AOP,但您也应该能够使用AOP来实现这一点(您的选择)。

所有您(应该)需要的是一个扩展SDR CacheManager implementation的“自定义”RedisCacheManager,类似于...

代码语言:javascript
运行
复制
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缓存基础设施继续进行,就像缓存”未命中“一样(即,在本例中,糟糕的Redis连接==缓存未命中)。

我知道这样的东西应该可以帮助您解决问题,因为我为GemFire和Pivotal的一个客户构建了一个类似的“自定义”CacheManager实现的原型。在该特定UC中,缓存“未命中”必须由应用程序域对象的“过期版本”触发,在该应用程序域对象中,生产环境中混合了通过Spring的缓存抽象连接到GemFire的新旧应用程序客户端。例如,在较新版本的应用程序中,应用程序域对象字段会发生变化。

无论如何,希望这篇文章对你有所帮助,或者给你更多的想法。

干杯!

票数 16
EN

Stack Overflow用户

发布于 2015-03-31 02:30:02

所以,我今天深入研究了核心的Spring Framework缓存抽象源代码,解决了另一个问题,似乎如果一个CacheErrorHandler被正确地实现了,那么一个有问题的Redis连接可能仍然会导致期望的行为,例如缓存“未命中”(由空值返回触发)。

有关更多详细信息,请参阅AbstractCacheInvoker源代码。

由于错误的Redis连接,cache.get(key)应该会导致异常,因此将调用异常处理程序……

代码语言:javascript
运行
复制
catch (RuntimeException e) {
    getErrorHandler().handleCacheGetError(e, cache, key);
    return null; // If the exception is handled, return a cache miss
}

如果CacheErrorHandler正确处理缓存"get“错误(并且不重新抛出/an异常),则将返回一个空值,表示缓存”未命中“。

票数 7
EN

Stack Overflow用户

发布于 2017-07-10 22:12:41

谢谢你@John Blum。我在Spring Boot中的解决方案如下。

代码语言:javascript
运行
复制
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.util.Assert;

import java.util.concurrent.Callable;

class CustomRedisCacheManager extends RedisCacheManager {
    private static Logger logger = LoggerFactory.getLogger(CustomRedisCacheManager.class);

    public CustomRedisCacheManager(RedisOperations redisOperations) {
        super(redisOperations);
    }

    @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 cache must not be null");
            this.delegate = redisCache;
        }

        @Override
        public String getName() {
            try {
                return delegate.getName();
            } catch (Exception e) {
                return handleException(e);
            }
        }

        @Override
        public Object getNativeCache() {
            try {
                return delegate.getNativeCache();
            } catch (Exception e) {
                return handleException(e);
            }
        }

        @Override
        public Cache.ValueWrapper get(Object key) {
            try {
                return delegate.get(key);
            } catch (Exception e) {
                return handleException(e);
            }
        }

        @Override
        public <T> T get(Object o, Class<T> aClass) {
            try {
                return delegate.get(o, aClass);
            } catch (Exception e) {
                return handleException(e);
            }
        }

        @Override
        public <T> T get(Object o, Callable<T> callable) {
            try {
                return delegate.get(o, callable);
            } catch (Exception e) {
                return handleException(e);
            }
        }

        @Override
        public void put(Object key, Object value) {
            try {
                delegate.put(key, value);
            } catch (Exception e) {
                handleException(e);
            }
        }

        @Override
        public ValueWrapper putIfAbsent(Object o, Object o1) {
            try {
                return delegate.putIfAbsent(o, o1);
            } catch (Exception e) {
                return handleException(e);
            }
        }

        @Override
        public void evict(Object o) {
            try {
                delegate.evict(o);
            } catch (Exception e) {
                handleException(e);
            }
        }

        @Override
        public void clear() {
            try {
                delegate.clear();
            } catch (Exception e) {
                handleException(e);
            }
        }

        private <T> T handleException(Exception e) {
            logger.error("handleException", e);
            return null;
        }
    }
}
代码语言:javascript
运行
复制
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration
public class RedisConfig {
    @Bean
    public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
        CustomRedisCacheManager redisCacheManager = new CustomRedisCacheManager(redisTemplate);
        redisCacheManager.setUsePrefix(true);
        return redisCacheManager;
    }
}
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29003786

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档