Cluster-Client-Erkennung und exponentielles Backoff (Valkey und Redis) OSS - Amazon ElastiCache

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Cluster-Client-Erkennung und exponentielles Backoff (Valkey und Redis) OSS

Wenn Sie im aktivierten Clustermodus eine Verbindung zu einem ElastiCache Valkey- oder OSS Redis-Cluster herstellen, muss die entsprechende Clientbibliothek clusterfähig sein. Die Clients müssen eine Zuordnung der Hash-Slots zu den entsprechenden Knoten im Cluster abrufen, um Anfragen an die richtigen Knoten zu senden und den zusätzlichen Leistungsaufwand bei der Bearbeitung von Cluster-Umleitungen zu vermeiden. Aus diesem Grund muss der Client in zwei verschiedenen Situationen eine vollständige Liste der Slots und der zugewiesenen Knoten erkennen:

  • Der Client ist initialisiert und muss die anfängliche Slot-Konfiguration auffüllen.

  • Eine MOVED Umleitung wird vom Server empfangen, z. B. bei einem Failover, bei dem alle vom früheren Primärknoten bedienten Steckplätze vom Replikat übernommen werden, oder bei einem Re-Sharding, wenn Steckplätze vom primären Quellknoten zum primären Zielknoten verschoben werden

Die Client-Erkennung erfolgt in der Regel durch Ausgabe eines CLUSTER NODE Befehls CLUSTER SLOT oder an den Valkey- oder Redis-Server. OSS Wir empfehlen CLUSTER SLOT diese Methode, da sie den Satz von Steckplatzbereichen und die zugehörigen Primär- und Replikatknoten an den Client zurückgibt. Dies erfordert kein zusätzliches Parsen durch den Client und ist effizienter.

Je nach Clustertopologie kann die Größe der Antwort auf den CLUSTER SLOT Befehl je nach Clustergröße variieren. Größere Cluster mit mehr Knoten erzeugen eine größere Antwort. Daher muss sichergestellt werden, dass die Anzahl der Clients, die die Cluster-Topologie ermitteln, nicht unbegrenzt zunimmt. Wenn beispielsweise die Client-Anwendung startet oder die Verbindung mit dem Server unterbrochen wird und die Cluster-Erkennung durchgeführt werden muss, besteht ein häufiger Fehler darin, dass die Client-Anwendung mehrere Wiederverbindungs- und Erkennungsanforderungen auslöst, ohne dass bei einem erneuten Versuch ein exponentielles Backoff erfolgt. Dies kann dazu führen, dass der Valkey- oder OSS Redis-Server über einen längeren Zeitraum nicht reagiert und die CPU Auslastung bei 100% liegt. Der Ausfall verlängert sich, wenn jeder CLUSTER SLOT Befehl eine große Anzahl von Knoten im Clusterbus verarbeiten muss. Wir haben in der Vergangenheit mehrere Client-Ausfälle aufgrund dieses Verhaltens in einer Reihe verschiedener Sprachen beobachtet, darunter Python (redis-py-cluster) und Java (Lettuce und Redisson).

In einem Serverless-Cache werden viele der Probleme automatisch behoben, da die angekündigte Cluster-Topologie statisch ist und aus zwei Einträgen besteht: einem Schreib- und einem Lese-Endpunkt. Die Cluster-Erkennung wird außerdem automatisch auf mehrere Knoten verteilt, wenn der Cache-Endpunkt verwendet wird. Die folgenden Empfehlungen sind jedoch nach wie vor nützlich.

Wir empfehlen Folgendes, um die Auswirkungen eines plötzlichen Zustroms von Verbindungs- und Erkennungsanforderungen zu minimieren:

  • Implementieren Sie einen Client-Verbindungspool mit einer begrenzten Größe, um die Anzahl der gleichzeitig eingehenden Verbindungen von der Client-Anwendung zu begrenzen.

  • Wenn der Client aufgrund eines Timeouts die Verbindung mit dem Server unterbricht, versuchen Sie es erneut mit exponentiellem Backoff mit Jitter. Dadurch wird vermieden, dass mehrere Clients den Server gleichzeitig überlasten.

  • Verwenden Sie die Anleitung unter Verbindungsendpunkte finden in ElastiCache, um den Cluster-Endpunkt zur Durchführung der Cluster-Erkennung zu finden. Dadurch verteilen Sie die Erkennungslast auf alle Knoten im Cluster (bis zu 90), anstatt einige fest codierte Seed-Knoten im Cluster zu erhalten.

Im Folgenden finden Sie einige Codebeispiele für die exponentielle Backoff-Wiederholungslogik in Redis-py, und Lettuce. PHPRedis

Beispiel 1 für Backoff-Logik: redis-py

Redis-py verfügt über einen integrierten Wiederholungsmechanismus, der unmittelbar nach einem Fehler einen erneuten Versuch vornimmt. Dieser Mechanismus kann durch das Argument aktiviert werden, das beim Erstellen eines Redis-Objekts angegeben wirdretry_on_timeout. OSS Hier demonstrieren wir einen benutzerdefinierten Wiederholungsmechanismus mit exponentiellem Backoff und Jitter. Wir haben eine Pull-Anfrage gesendet, um exponentielles Backoff in redis-py (#1494) nativ zu implementieren. Künftig ist eine manuelle Implementierung möglicherweise nicht mehr erforderlich.

def run_with_backoff(function, retries=5): base_backoff = 0.1 # base 100ms backoff max_backoff = 10 # sleep for maximum 10 seconds tries = 0 while True: try: return function() except (ConnectionError, TimeoutError): if tries >= retries: raise backoff = min(max_backoff, base_backoff * (pow(2, tries) + random.random())) print(f"sleeping for {backoff:.2f}s") sleep(backoff) tries += 1

Sie können dann den folgenden Code verwenden, um einen Wert festzulegen:

client = redis.Redis(connection_pool=redis.BlockingConnectionPool(host=HOST, max_connections=10)) res = run_with_backoff(lambda: client.set("key", "value")) print(res)

Abhängig von Ihrem Workload möchten Sie möglicherweise den Basis-Backoff-Wert von 1 Sekunde auf einige Dutzende oder Hunderte von Millisekunden für latenzempfindliche Workloads ändern.

Beispiel 2 für Backoff-Logik: PHPRedis

PHPRedisverfügt über einen integrierten Wiederholungsmechanismus, der (nicht konfigurierbar) maximal 10 Wiederholungen vornimmt. Es gibt eine konfigurierbare Verzögerung zwischen den Wiederholungsversuchen (mit einem Jitter ab dem zweiten Versuch). Weitere Informationen finden Sie im folgenden Codebeispiel. Wir haben eine Pull-Anfrage zur nativen Implementierung des exponentiellen Backoffs in PHPredis(#1986) eingereicht, die inzwischen zusammengeführt und dokumentiert wurde. Für diejenigen, die die neueste Version von verwendenPHPRedis, ist eine manuelle Implementierung nicht erforderlich, aber wir haben hier die Referenz für die Versionen früherer Versionen aufgenommen. Im Folgenden finden Sie zunächst ein Codebeispiel, das die Verzögerung des Wiederholungsmechanismus konfiguriert:

$timeout = 0.1; // 100 millisecond connection timeout $retry_interval = 100; // 100 millisecond retry interval $client = new Redis(); if($client->pconnect($HOST, $PORT, $timeout, NULL, $retry_interval) != TRUE) { return; // ERROR: connection failed } $client->set($key, $value);

Beispiel 3 für Backoff-Logik: Lettuce

Lettuce verfügt über integrierte Wiederholungsmechanismen, die auf den exponentiellen Backoff-Strategien basieren, wie im Beitrag Exponentielles Backoff und Jitter beschrieben. Im Folgenden finden Sie einen Codeauszug, der den vollständigen Jitter-Ansatz zeigt:

public static void main(String[] args) { ClientResources resources = null; RedisClient client = null; try { resources = DefaultClientResources.builder() .reconnectDelay(Delay.fullJitter( Duration.ofMillis(100), // minimum 100 millisecond delay Duration.ofSeconds(5), // maximum 5 second delay 100, TimeUnit.MILLISECONDS) // 100 millisecond base ).build(); client = RedisClient.create(resources, RedisURI.create(HOST, PORT)); client.setOptions(ClientOptions.builder() .socketOptions(SocketOptions.builder().connectTimeout(Duration.ofMillis(100)).build()) // 100 millisecond connection timeout .timeoutOptions(TimeoutOptions.builder().fixedTimeout(Duration.ofSeconds(5)).build()) // 5 second command timeout .build()); // use the connection pool from above example } finally { if (connection != null) { connection.close(); } if (client != null){ client.shutdown(); } if (resources != null){ resources.shutdown(); } } }