Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.
Detección de clústeres por parte del cliente y retroceso exponencial (Valkey y Redis OSS)
Al conectarse a un clúster OSS de ElastiCache Valkey o Redis con el modo de clúster activado, la biblioteca cliente correspondiente debe reconocer el clúster. Los clientes deben obtener un mapa de los slots hash de los nodos correspondientes del clúster para poder enviar las solicitudes a los nodos correctos, y evitar así la sobrecarga de rendimiento que supone gestionar las redirecciones del clúster. Como resultado, el cliente debe descubrir (o detectar) una lista completa de los slots y los nodos mapeados en dos situaciones diferentes:
El cliente se inicializa y debe completar la configuración inicial de los slots.
Se recibe una redirección MOVED del servidor; por ejemplo, en una conmutación por error, cuando la réplica se hace cargo de todos los slots atendidos por el nodo principal anterior, o en una nueva partición, cuando los slots se mueven del nodo principal de origen al nodo principal de destino.
Por lo general, el proceso de detección por parte del cliente se lleva a cabo enviando un comando CLUSTER SLOT o CLUSTER NODE al servidor de Valkey o Redis OSS. Recomendamos el método CLUSTER SLOT porque devuelve al cliente el conjunto de rangos de slots, así como los nodos principales y de réplica asociados. Es un método que no requiere un análisis adicional por parte del cliente y es más eficaz.
En función de la topología del clúster, el tamaño de la respuesta al comando CLUSTER SLOT puede variar según el tamaño del clúster. Los clústeres más grandes y con más nodos producen una respuesta mayor. Por lo tanto, es importante asegurarse de que la cantidad de clientes que llevan a cabo la detección de la topología del clúster no aumente de forma ilimitada. Por ejemplo, cuando la aplicación cliente se inicia o pierde la conexión con el servidor y debe realizar una detección de clústeres, un error común es que la aplicación cliente desencadene varias solicitudes de reconexión y detección sin añadir un retroceso exponencial con el nuevo intento. Esto puede hacer que el servidor de Valkey o Redis OSS deje de responder durante mucho tiempo, con un uso de la CPU del 100 %. La interrupción se prolonga si cada comando CLUSTER SLOT debe procesar una gran cantidad de nodos en el bus del clúster. Hemos observado varias interrupciones en los clientes en el pasado debido a este comportamiento en varios lenguajes diferentes, incluidos Python (redis-py-cluster) y Java (Lettuce y Redisson).
En una caché sin servidor, muchos de los problemas se mitigan automáticamente porque la topología de clúster expuesta es estática y consta de dos entradas: un punto de conexión de escritura y otro de lectura. La detección de clústeres también se distribuye automáticamente entre varios nodos cuando se utiliza el punto de conexión de la caché. Sin embargo, las siguientes recomendaciones siguen siendo útiles.
A fin de mitigar el impacto causado por una entrada repentina de solicitudes de conexión y detección, recomendamos lo siguiente:
Implemente un grupo de conexiones de cliente con un tamaño finito a fin de limitar el número de conexiones entrantes simultáneas desde la aplicación cliente.
Cuando el cliente se desconecte del servidor debido al tiempo de espera, vuelva a intentarlo con retroceso exponencial y fluctuación. Esto evitará que varios clientes sobrecarguen el servidor al mismo tiempo.
Utilice la guía en Búsqueda de puntos finales de conexión en ElastiCache para encontrar el punto de conexión del clúster que necesitará a fin de realizar la detección del clúster. De este modo, distribuirá la carga de detección entre todos los nodos del clúster (hasta 90), en lugar de centrarse en unos pocos nodos raíz codificados del clúster.
A continuación, se muestran algunos ejemplos de código de la lógica de reintentos de retroceso exponencial en redis-py y Lettuce. PHPRedis
Ejemplo 1 de lógica de retroceso: redis-py
Redis-py tiene un mecanismo de reintento incorporado: se lleva a cabo un reintento inmediatamente después de un error. Este mecanismo se puede activar mediante el argumento retry_on_timeout
proporcionado al crear un objeto de Redis OSS
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
Luego, puede utilizar el siguiente código para establecer un valor:
client = redis.Redis(connection_pool=redis.BlockingConnectionPool(host=HOST, max_connections=10)) res = run_with_backoff(lambda: client.set("key", "value")) print(res)
En función de la carga de trabajo, es posible que desee cambiar el valor del retroceso base (de 1 segundo a unas pocas decenas o cientos de milisegundos) para las cargas de trabajo sensibles a la latencia.
Ejemplo 2 de lógica de retroceso: PHPRedis
PHPRedis tiene un mecanismo de reintento integrado que lo reintenta un máximo de 10 veces (no configurable). Hay un retraso configurable entre los intentos (con una fluctuación a partir del segundo reintento). Para obtener más información, consulte el siguiente código de muestra
$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);
Ejemplo 3 de lógica de retroceso: Lettuce
Lettuce tiene mecanismos de reintento incorporados que emplean las estrategias de retroceso exponencial descritas en la publicación Exponential Backoff and 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(); } } }