Descubrimiento de clientes en clústeres y retroceso exponencial (Valkey y Redis) OSS - Amazon ElastiCache

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.

Descubrimiento de clientes en clústeres y retroceso exponencial (Valkey y Redis) OSS

Al conectarse a un clúster de ElastiCache Valkey o Redis con el modo de OSS clúster activado, la biblioteca cliente correspondiente debe ser compatible con los clústeres. 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 MOVED redirección del servidor, por ejemplo, cuando se produce una conmutación por error, cuando todas las ranuras servidas por el nodo principal anterior pasan a manos de la réplica, o se vuelve a fragmentar cuando las ranuras se mueven del nodo principal de origen al nodo principal de destino

Por lo general, la detección de clientes se realiza mediante la emisión de un CLUSTER NODE comando CLUSTER SLOT or al servidor Valkey o Redis. OSS Recomendamos CLUSTER SLOT este método porque devuelve al cliente el conjunto de rangos de ranuras y los nodos principal 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.

Según la topología del clúster, el tamaño de la respuesta al CLUSTER SLOT comando puede variar en función del 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 OSS servidor Valkey o Redis deje de responder durante un período prolongado, con una utilización del 100%. CPU La interrupción se prolonga si cada CLUSTER SLOT comando 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 retry_on_timeout argumento que se proporciona al crear un objeto Redis. OSS Aquí mostramos un mecanismo de reintento personalizado con retroceso exponencial y fluctuación. Hemos enviado una solicitud de extracción para implementar de forma nativa el retroceso exponencial en redis-py (1494). En el futuro, puede que no sea necesario implementarlo manualmente.

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

PHPRedistiene 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. Hemos enviado una solicitud de cambios para implementar de forma nativa el retroceso exponencial en PHPredis(#1986), que desde entonces se ha fusionado y documentado. Si utilizas la última versión dePHPRedis, no será necesario implementarla manualmente, pero aquí incluimos la referencia para las versiones anteriores. De momento, aquí tiene un ejemplo de código para configurar el retraso del mecanismo de reintento:

$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. A continuación, puede ver un fragmento de código con el método de fluctuación total:

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