sql >> Base de Datos >  >> NoSQL >> Redis

Cómo deshabilitar el almacenamiento en caché de Redis en tiempo de ejecución si falla la conexión de Redis

Reduzcamos esto un poco. Su aplicación utiliza el almacenamiento en caché (implementado con Redis). Si la conexión de Redis está obsoleta/cerrada o no, entonces desea que la aplicación omita el almacenamiento en caché y (presumiblemente) vaya directamente a un almacén de datos subyacente (por ejemplo, RDBMS). La lógica del servicio de la aplicación puede parecerse 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);
    }
    ...
}

Todo lo que importa en Caching Abstraction de Spring Core para determinar un "fallo" de Cache es que el valor devuelto es nulo. Como tal, Spring Caching Infrastructure luego procederá a llamar al método de servicio real (es decir, getCustomer). Tenga en cuenta que al devolver la llamada getCustomerRepo().load(customerId), también debe manejar el caso en el que la infraestructura de almacenamiento en caché de Spring intenta ahora almacenar en caché el valor.

En el espíritu de mantenerlo simple , lo haremos sin AOP, pero también debería poder lograrlo usando AOP (su elección).

Todo lo que (debe) necesitar es un RedisCacheManager "personalizado" que amplíe la implementación de 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;
            }
        }
    }
}

Por lo tanto, si Redis no está disponible, tal vez lo mejor que puede hacer es registrar el problema y permitir que ocurra la invocación del servicio. Claramente, esto dificultará el desempeño, pero al menos creará conciencia de que existe un problema. Claramente, esto podría vincularse a un sistema de notificación más robusto, pero es un ejemplo crudo de las posibilidades. Lo importante es que su Servicio permanece disponible mientras que los otros servicios (por ejemplo, Redis) de los que depende el servicio de la aplicación pueden haber fallado.

En esta implementación (frente a mi explicación anterior), elegí delegar a la implementación real subyacente de RedisCache para permitir que ocurra la excepción, sabiendo muy bien que existe un problema con Redis, y para que pueda manejar la excepción de manera adecuada. Sin embargo, si está seguro de que la excepción está relacionada con un problema de conexión tras la inspección, puede devolver "null" para permitir que Spring Caching Infrastructure proceda como si fuera un "fallo" de caché (es decir, mala conexión de Redis ==fallo de caché, en este caso).

Sé que algo como esto debería solucionar su problema, ya que construí un prototipo similar de una implementación de CacheManager "personalizada" para GemFire ​​y uno de los clientes de Pivotal. En esa UC en particular, la "falla" de caché tuvo que ser desencadenada por una "versión desactualizada" del objeto de dominio de la aplicación donde la producción tenía una combinación de clientes de aplicaciones más nuevos y más antiguos que se conectaban a GemFire ​​a través de Caching Abstraction de Spring. Los campos del objeto del dominio de la aplicación cambiarían en las versiones más nuevas de la aplicación, por ejemplo.

De todos modos, espero que esto te ayude o te dé más ideas.

¡Salud!