Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Découverte des clients du cluster et ralentissement exponentiel (Valkey et Redis) OSS
Lorsque vous vous connectez à un OSS cluster ElastiCache Valkey ou Redis en mode cluster activé, la bibliothèque cliente correspondante doit être consciente du cluster. Les clients doivent obtenir une carte des emplacements de hachage sur les nœuds correspondants du cluster afin d’envoyer des demandes aux nœuds appropriés et d’éviter la surcharge de performances liée à la gestion des redirections du cluster. Par conséquent, le client doit découvrir la liste complète des emplacements et des nœuds mappés dans deux cas différents :
Le client est initialisé et doit renseigner la configuration initiale des emplacements.
Une MOVED redirection est reçue du serveur, par exemple en cas de basculement lorsque tous les emplacements desservis par l'ancien nœud principal sont pris en charge par la réplique, ou en cas de repartitionnement lorsque les emplacements sont déplacés du nœud principal source vers le nœud principal cible
La découverte du client se fait généralement en émettant une CLUSTER NODE commande CLUSTER SLOT ou au serveur Valkey ou RedisOSS. Nous recommandons CLUSTER SLOT cette méthode car elle renvoie au client l'ensemble des plages de slots ainsi que les nœuds principaux et répliques associés. Cette méthode ne nécessite pas d’analyse supplémentaire de la part du client. Elle est également plus efficace.
En fonction de la topologie du cluster, la taille de la réponse à la CLUSTER SLOT commande peut varier en fonction de la taille du cluster. Les clusters de plus grande taille dotés d’un plus grand nombre de nœuds produisent une réponse plus longue. Par conséquent, il est important de veiller à ce que le nombre de clients qui effectuent la découverte de la topologie du cluster n’augmente pas de manière illimitée. Par exemple, lorsque l’application client démarre ou perd la connexion avec le serveur et doit effectuer une découverte de clusters, une erreur courante est qu’elle lance plusieurs demandes de reconnexion et de découverte sans ajouter de backoff exponentiel lors d’une nouvelle tentative. Cela peut empêcher le OSS serveur Valkey ou Redis de répondre pendant une période prolongée, avec une CPU utilisation à 100 %. La panne est prolongée si chaque CLUSTER SLOT commande doit traiter un grand nombre de nœuds dans le bus du cluster. Nous avons observé plusieurs pannes de clients dans le passé en raison de ce comportement dans différents langages, notamment Python (redis-py-cluster) et Java (Lettuce et Redisson).
Dans un cache sans serveur, de nombreux problèmes sont automatiquement atténués, car la topologie du cluster annoncée est statique et comprend deux entrées : un point de terminaison d’écriture et un point de terminaison de lecture. De plus, la découverte de clusters est automatiquement répartie sur plusieurs nœuds lors de l’utilisation du point de terminaison du cache. Les recommandations suivantes restent toutefois utiles.
Pour atténuer l’impact provoqué par un afflux soudain de demandes de connexion et de découverte, nous recommandons ce qui suit :
Implémentez un groupe de connexions client de taille limitée pour limiter le nombre de connexions entrantes simultanées en provenance de l’application client.
Lorsque le client se déconnecte du serveur en raison d’un délai d’expiration, réessayez en ajoutant un backoff exponentiel avec instabilité. Cela permet d’éviter que plusieurs clients ne surchargent le serveur en même temps.
Consultez Recherche de points de terminaison de connexion dans ElastiCache pour trouver le point de terminaison de cluster afin d’effectuer la découverte de clusters. En agissant ainsi, vous répartissez la charge de découverte sur tous les nœuds du cluster (jusqu’à 90) au lieu de toucher quelques nœuds de départ codés en dur du cluster.
Voici quelques exemples de code pour la logique de réessai exponentielle dans redis-py,, et Lettuce. PHPRedis
Exemple de logique avec backoff 1 : redis-py
redis-py possède un mécanisme de nouvelle tentative intégré qui permet d’effectuer une nouvelle tentative immédiatement après un échec. Ce mécanisme peut être activé via l'retry_on_timeout
argument fourni lors de la création d'un OSS objet Redis
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
Vous pouvez ensuite utiliser le code suivant pour définir une valeur :
client = redis.Redis(connection_pool=redis.BlockingConnectionPool(host=HOST, max_connections=10)) res = run_with_backoff(lambda: client.set("key", "value")) print(res)
En fonction de votre charge de travail, vous souhaiterez peut-être modifier la valeur de backoff de base de 1 seconde à quelques dizaines ou centaines de millisecondes pour les charges de travail sensibles à la latence.
Exemple de logique de rétrogradation 2 : PHPRedis
PHPRedispossède un mécanisme intégré qui permet de réessayer 10 fois (non configurable) au maximum. Il est possible de configurer un délai entre les tentatives (avec une instabilité à partir de la deuxième tentative). Pour plus d’informations, consultez l’exemple de code
$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);
Exemple de logique avec backoff 3 : Lettuce
Lettuce possède des mécanismes de nouvelle tentative intégrés basés sur les stratégies de backoff exponentiel décrites dans l’article Backoff exponentiel et instabilité
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(); } } }