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 Redis-OSS-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. im Fall eines Failovers, bei dem alle vom ehemaligen Primärknoten bereitgestellten Slots vom Replikat übernommen werden, oder beim Re-Sharding, wenn Slots vom primären Quellknoten zum primären Zielknoten verschoben werden.
Die Client-Erkennung erfolgt normalerweise durch Ausgabe eines CLUSTER SLOT- oder CLUSTER NODE-Befehls an den Valkey- oder Redis OSS-Server. Wir empfehlen die CLUSTER SLOT-Methode, da sie das Set der Slot-Bereiche 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.
Abhängig von der Cluster-Topologie kann die Größe der Antwort auf den Befehl CLUSTER SLOT 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 Redis OSS-Server bei einer CPU-Auslastung von 100% über einen längeren Zeitraum nicht reagiert. Der Ausfall verlängert sich, wenn jeder CLUSTER SLOT-Befehl eine große Anzahl von Knoten im Cluster-Bus verarbeiten muss. Aufgrund dieses Verhaltens haben wir in der Vergangenheit mehrere Client-Ausfälle in verschiedenen 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 retry_on_timeout
Argument aktiviert werden, das beim Erstellen eines Redis-OSS-Objekts angegeben wird.
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
PHPRedis verfügt über einen integrierten Wiederholungsmechanismus, der (nicht konfigurierbar) maximal zehnmal versucht. Es gibt eine konfigurierbare Verzögerung zwischen den Wiederholungsversuchen (mit einem Jitter ab dem zweiten Versuch). Weitere Informationen finden Sie im folgenden Codebeispiel
$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
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(); } } }