Redis
 sql >> Datenbank >  >> NoSQL >> Redis

So deaktivieren Sie das Redis-Caching zur Laufzeit, wenn die Redis-Verbindung fehlgeschlagen ist

Lassen Sie uns das ein wenig einkochen. Ihre Anwendung verwendet Caching (implementiert mit Redis). Wenn die Redis-Verbindung veraltet/geschlossen oder anderweitig ist, möchten Sie, dass die Anwendung das Caching umgeht und (vermutlich) direkt zu einem zugrunde liegenden Datenspeicher (z. B. RDBMS) wechselt. Die Anwendungsdienstlogik könnte ähnlich aussehen wie...

@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);
    }
    ...
}

Alles, was in der Caching-Abstraktion von Spring Core wichtig ist, um einen Cache-„Miss“ festzustellen, ist, dass der zurückgegebene Wert null ist. Als solches fährt Spring Caching Infrastructure dann mit dem Aufrufen der eigentlichen Service-Methode (d. h. getCustomer) fort. Denken Sie bei der Rückgabe des getCustomerRepo().load(customerId)-Aufrufs daran, dass Sie auch den Fall behandeln müssen, in dem die Caching-Infrastruktur von Spring versucht, den Wert jetzt zwischenzuspeichern.

Ganz im Sinne von keep it simple , werden wir auf AOP verzichten, aber Sie sollten dies auch mit AOP erreichen können (Ihre Wahl).

Alles, was Sie brauchen (sollten), ist ein "benutzerdefinierter" RedisCacheManager, der die SDR-CacheManager-Implementierung erweitert, so etwas wie ...

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;
            }
        }
    }
}

Wenn also Redis nicht verfügbar ist, ist es vielleicht das Beste, das Problem zu protokollieren und den Dienstaufruf zuzulassen. Natürlich wird dies die Leistung beeinträchtigen, aber zumindest das Bewusstsein dafür schärfen, dass ein Problem besteht. Natürlich könnte dies in ein robusteres Benachrichtigungssystem eingebunden werden, aber es ist ein grobes Beispiel für die Möglichkeiten. Wichtig ist, dass Ihr Dienst verfügbar bleibt, während die anderen Dienste (z. B. Redis), von denen der Anwendungsdienst abhängig ist, möglicherweise ausgefallen sind.

In dieser Implementierung (im Gegensatz zu meiner vorherigen Erklärung) habe ich mich dafür entschieden, an die zugrunde liegende, tatsächliche RedisCache-Implementierung zu delegieren, um die Ausnahme auftreten zu lassen, dann genau zu wissen, dass ein Problem mit Redis besteht, und damit Sie die Ausnahme angemessen behandeln können. Wenn Sie sich jedoch sicher sind, dass die Ausnahme mit einem Verbindungsproblem bei der Inspektion zusammenhängt, können Sie "null" zurückgeben, damit die Spring Caching Infrastructure so fortfährt, als wäre es ein Cache-"Miss" (d. h. schlechte Redis-Verbindung ==Cache-Miss, in diesem Fall).

Ich weiß, dass so etwas Ihrem Problem helfen sollte, da ich einen ähnlichen Prototyp einer "benutzerdefinierten" CacheManager-Implementierung für GemFire ​​und einen der Kunden von Pivotal erstellt habe. In diesem speziellen UC musste der Cache-„Miss“ durch eine „veraltete Version“ des Anwendungsdomänenobjekts ausgelöst werden, wo die Produktion eine Mischung aus neueren und älteren Anwendungsclients hatte, die sich über Springs Caching Abstraction mit GemFire ​​verbinden. Die Objektfelder der Anwendungsdomäne würden sich beispielsweise in neueren Versionen der App ändern.

Wie auch immer, ich hoffe, das hilft dir oder gibt dir weitere Ideen.

Prost!